package com.yanqu.road.server.manager.activity.jiuzhou.activity;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.yanqu.road.entity.DataStatus;
import com.yanqu.road.entity.activity.ActivityConditionInfo;
import com.yanqu.road.entity.activity.ActivityInfo;
import com.yanqu.road.entity.activity.jiuzhou.*;
import com.yanqu.road.entity.activity.jiuzhou.config.JiuZhouRoundConfig;
import com.yanqu.road.entity.activity.jiuzhou.config.JiuZhouSkillConfig;
import com.yanqu.road.entity.activity.jiuzhou.game.*;
import com.yanqu.road.entity.activity.jiuzhou.helper.show.*;
import com.yanqu.road.entity.activity.jiuzhou.guess.CrossJiuZhouGuessData;
import com.yanqu.road.entity.activity.jiuzhou.guess.CrossJiuZhouUserGuessData;
import com.yanqu.road.entity.activity.jiuzhou.round.CrossJiuZhouRoundData;
import com.yanqu.road.entity.config.goods.GoodsInfo;
import com.yanqu.road.entity.config.patrons.PatronsInfo;
import com.yanqu.road.entity.enums.activity.jiuzhou.*;
import com.yanqu.road.entity.enums.eChatContentType;
import com.yanqu.road.entity.enums.eGamePlayerEventType;
import com.yanqu.road.entity.enums.eMessageType;
import com.yanqu.road.entity.enums.eSceneType;
import com.yanqu.road.entity.log.cross.jiuzhou.LogJiuZhouBattle;
import com.yanqu.road.entity.log.cross.jiuzhou.LogJiuZhouBattleSettle;
import com.yanqu.road.entity.log.cross.jiuzhou.LogJiuZhouSignUp;
import com.yanqu.road.entity.player.CrossUserBaseInfo;
import com.yanqu.road.entity.scene.ScenePlayerInfo;
import com.yanqu.road.entity.scene.UserScene;
import com.yanqu.road.logic.bussiness.activity.JiuZhouBussiness;
import com.yanqu.road.logic.helper.MathHelper;
import com.yanqu.road.entity.player.UserBaseInfo;
import com.yanqu.road.logic.helper.WeightHelper;
import com.yanqu.road.logic.pb.JiuZhouPb;
import com.yanqu.road.logic.pb.PlayerBasePb;
import com.yanqu.road.message.YanQuMessage;
import com.yanqu.road.pb.activity.jiuzhou.CrossJiuZhouProto;
import com.yanqu.road.pb.activity.jiuzhou.JiuZhouProto;
import com.yanqu.road.pb.cross.activity.CrossActivityProto;
import com.yanqu.road.pb.patrons.PatronsProto;
import com.yanqu.road.pb.worldmessage.WorldMessageProto;
import com.yanqu.road.server.CrossBattleServer;
import com.yanqu.road.server.manager.activity.ActivityChatMgr;
import com.yanqu.road.server.manager.activity.ActivityMgr;
import com.yanqu.road.entity.activity.jiuzhou.rank.CrossJiuZhouRankListModel;
import com.yanqu.road.entity.activity.jiuzhou.rank.JiuZhouRank;
import com.yanqu.road.entity.activity.jiuzhou.rank.JiuZhouRoomBattleRankSort;
import com.yanqu.road.server.manager.activity.jiuzhou.CrossJiuZhouMgr;
import com.yanqu.road.server.manager.activity.jiuzhou.chat.CrossJiuZhouChatLogic;
import com.yanqu.road.server.manager.config.GoodsMgr;
import com.yanqu.road.server.manager.config.PatronsMgr;
import com.yanqu.road.server.manager.log.CrossAutoLogMgr;
import com.yanqu.road.server.manager.npc.CrossNpcUserMgr;
import com.yanqu.road.server.manager.player.WorldMessageMgr;
import com.yanqu.road.server.manager.scene.CrossSceneMgr;
import com.yanqu.road.server.manager.user.CrossUserMgr;
import com.yanqu.road.server.manger.ServerListMgr;
import com.yanqu.road.server.protocol.ClientProtocol;
import com.yanqu.road.server.protocol.GameErrorCode;
import com.yanqu.road.server.protocol.GameProtocol;
import com.yanqu.road.server.protocol.Protocol;
import com.yanqu.road.utils.YanQuMessageUtils;
import com.yanqu.road.utils.config.Config;
import com.yanqu.road.utils.date.DateHelper;
import com.yanqu.road.utils.property.Property;
import com.yanqu.road.utils.property.PropertyHelper;
import com.yanqu.road.utils.string.StringUtils;
import org.apache.mina.util.ConcurrentHashSet;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.yanqu.road.entity.enums.activity.jiuzhou.eJiuZhouAttributeEnum.*;
import static com.yanqu.road.server.logic.MessageHelper.*;

/**
 * 九州棋局
 */
public class CrossJiuZhouActivity extends CrossJiuZhouBaseActivity {

    /**
     * 用户数据
     * userId,用户数据
     */
    private Map<Long, CrossJiuZhouUserData> userDataMap = new ConcurrentHashMap<>();

    /**
     * 房间数据
     * 房间id,房间数据
     */
    private Map<Integer, CrossJiuZhouRoomData> roomDataMap = new ConcurrentHashMap<>();

    /**
     * 房间id数据Map 记得初始化 Map<RoundId,List<RoomId>>
     */
    private Map<Integer,List<Integer>> roundRoomIdMap = new ConcurrentHashMap<>();

    /**
     * 玩家部署数据
     * 房间id,用户id,List<部署数据>
     */
    private Map<Integer, Map<Long, List<CrossJiuZhouDeployData>>> deployDataMap = new ConcurrentHashMap<>();

    /**
     * 每轮淘汰赛的竞猜总数据 Map<轮次,每轮竞猜的数据 >
     */
    private Map<Integer, CrossJiuZhouGuessData> roundGuessDataMap = new ConcurrentHashMap<>();

    /**
     * 所有玩家在本次活动内的所有竞猜数据
     */
    private Map<Long, CrossJiuZhouUserGuessData> userGuessDataMap = new ConcurrentHashMap<>();

    /**
     * 当前最大的房间id
     */
    private AtomicInteger maxRoomId = new AtomicInteger(0);


    /**
     * 镜像id
     */
    private AtomicLong mirrorUserId = new AtomicLong(System.currentTimeMillis());

    /**
     * 战斗记录
     */
    private Map<Integer, List<CrossJiuZhouGameRoundRecordData>> gameRoundRecordDataMap = new ConcurrentHashMap<>();

    /**
     * 声援记录，纯内存
     */
    private Map<Integer, Map<Long, Map<Long, Integer>>> supportCntMap = new ConcurrentHashMap<>();

    /**
     * 聊天
     */
    private CrossJiuZhouChatLogic chatLogic;

    /**
     * 获取当前轮次
     */
    public int getCurRoundByUserId(long userId) {
        CrossJiuZhouUserData userData = getUserData(userId);
        if (userData == null) {
            return 0;
        }
        CrossJiuZhouRoundData roundData = roundDataMap.get(userData.getZoneType());
        return roundData == null ? 0 : roundData.getCurRoundId();
    }

    //活动开始前所有活跃玩家的赚钱数据 Map<serverId,List<赚钱>>
    public Map<Integer,List<BigInteger>> userAbilityMap = new ConcurrentHashMap<>();
    //上次请求服务器时间
    public Map<Long,Long> lastRequestServerTimeMap = new ConcurrentHashMap<>();
    //活动当前阶段信息 <zoneId,roundData>
    public Map<Integer, CrossJiuZhouRoundData> roundDataMap = new ConcurrentHashMap<>();

    //积分赛排行榜
    public Map<Integer,CrossJiuZhouRankListModel> scoreRoundRankMap = new ConcurrentHashMap<>();
    //淘汰赛排行
    public Map<Integer,CrossJiuZhouRankListModel> outRoundRankMap = new ConcurrentHashMap<>();


    /**
     * 房间战斗实时排名(不入库，重载时重排)
     */
    private Map<Integer, CrossJiuZhouRankListModel> roomBattleRankMap = new ConcurrentHashMap<>();

    /**
     * 人气榜单 (不入库，重载时重排)   "轮次-赛区"，榜单
     */
    private Map<String, CrossJiuZhouRankListModel> popularityRankMap = new ConcurrentHashMap<>();

    private String getPopularityRankKey(int zoneType, int round) {
        return zoneType + "-" + round;
    }

    /**
     * 玩家锁
     */
    private static final Map<Long, Object> playerLockMap = new ConcurrentHashMap<>();

    /**
     * 获取玩家锁
     */
    private static Object getPlayerLock(long userId) {
        if (!playerLockMap.containsKey(userId)) {
            synchronized (playerLockMap) {
                if (!playerLockMap.containsKey(userId)) {
                    playerLockMap.put(userId, new Object());
                }
            }
        }
        return playerLockMap.get(userId);
    }



    /**
     * 房间锁(战场)
     */
    private static final Map<Integer, Object> roomLockMap = new ConcurrentHashMap<>();

    /**
     * 获取房间锁
     */
    private static Object getRoomLock(int roomId) {
        if (!roomLockMap.containsKey(roomId)) {
            synchronized (roomLockMap) {
                if (!roomLockMap.containsKey(roomId)) {
                    roomLockMap.put(roomId, new Object());
                }
            }
        }
        return roomLockMap.get(roomId);
    }

    public CrossJiuZhouActivity(ActivityInfo activityInfo) {
        super(activityInfo);
        // 加载数据
        userDataMap = JiuZhouBussiness.getCrossJiuZhouUserDataMap(activityInfo.getActivityId());
        deployDataMap = JiuZhouBussiness.getCrossJiuZhouDeployDataMap(activityInfo.getActivityId());
        roomDataMap = JiuZhouBussiness.getCrossJiuZhouRoomDataMap(activityInfo.getActivityId());
        // 处理下房间数据
        int curMaxRoomId = 0;
        for (CrossJiuZhouRoomData roomData : roomDataMap.values()) {
            addToRoomIdMap(roomData.getRound(), roomData.getRoomId());
            curMaxRoomId = Math.max(curMaxRoomId, roomData.getRoomId());
        }
        // 最大的房间号id
        maxRoomId = new AtomicInteger(curMaxRoomId);

        gameRoundRecordDataMap = JiuZhouBussiness.getCrossJiuZhouGameRoundRecordDataMap(activityInfo.getActivityId());

        roundDataMap = JiuZhouBussiness.getCrossJiuZhouRoundDataMap(activityInfo.getActivityId());
        roundGuessDataMap = JiuZhouBussiness.getCrossJiuZhouGuessDataMap(activityInfo.getActivityId());
        userGuessDataMap = JiuZhouBussiness.getCrossJiuZhouUserGuessMapData(activityInfo.getActivityId());
        //检查初始化活动阶段数据 不走定时器 需要直接初始化
        synchronized (roundDataMap){
            for (eJiuZhouGameZoneType type:eJiuZhouGameZoneType.values()){
                if (roundDataMap.containsKey(type.getValue())){
                    continue;
                }
                //如果这个赛区没有开启 也不给他生成新的
                if (!config.isGameZoneOpen(type.getValue())){
                    continue;
                }
                CrossJiuZhouRoundData roundData = new CrossJiuZhouRoundData();
                roundData.setActivityId(activityInfo.getActivityId());
                roundData.setZoneType(type.getValue());
                roundData.setCurRoundId(0);
                roundData.setCurRoundState(eJiuZhouRoundTime.serverMatching.getValue());
                roundData.setRoundType(eJiuZhouRoundType.noOpen.getValue());
                roundData.setIsLockAbility(0);
                roundData.setLockAbility(BigInteger.ZERO);
                roundData.setRoundPromotionNumMap(new ConcurrentHashMap<>());
                roundData.setInsertOption();
                roundDataMap.put(type.getValue(),roundData);
            }
        }
        List<JiuZhouRank> jiuZhouRankList = JiuZhouBussiness.getJiuZhouRankList(activityInfo.getActivityId());
        // 初始化所有榜单
        initAllRank(jiuZhouRankList);
        // 初始化聊天
        chatLogic = new CrossJiuZhouChatLogic(activityInfo);
    }

    private void initAllRank(List<JiuZhouRank> jiuZhouRankList) {
        long nowTime = System.currentTimeMillis();

        //初始化排行榜
        for (eJiuZhouGameZoneType type:eJiuZhouGameZoneType.values()){
            //如果这个赛区没有开启 也不给他生成新的
            if (!config.isGameZoneOpen(type.getValue())){
                continue;
            }
            scoreRoundRankMap.put(type.getValue(),new CrossJiuZhouRankListModel());
            outRoundRankMap.put(type.getValue(),new CrossJiuZhouRankListModel());
        }
        Map<Integer, Map<Integer, List<JiuZhouRank>>> map = new HashMap<>();
        for (JiuZhouRank jiuZhouRank : jiuZhouRankList) {
            Map<Integer, List<JiuZhouRank>> map1 = map.getOrDefault(jiuZhouRank.getType(), new HashMap<>());
            List<JiuZhouRank> list = map1.getOrDefault(jiuZhouRank.getZoneType(), new ArrayList<>());
            list.add(jiuZhouRank);
            map1.put(jiuZhouRank.getZoneType(), list);
            map.put(jiuZhouRank.getType(), map1);
        }
        for (Map.Entry<Integer, Map<Integer, List<JiuZhouRank>>> entry : map.entrySet()) {
            if (entry.getKey() == eJiuZhouRoundType.score.getValue()) {
                for (Map.Entry<Integer, List<JiuZhouRank>> entry1 : entry.getValue().entrySet()) {
                    CrossJiuZhouRankListModel crossJiuZhouRankListModel = scoreRoundRankMap.get(entry1.getKey());
                    if (crossJiuZhouRankListModel != null) {
                        crossJiuZhouRankListModel.batchRankChange(entry1.getValue());
                        //特殊处理先 全是insert...
                        for (JiuZhouRank rank:crossJiuZhouRankListModel.getRankList()){
                            rank.setNoneOption();
                        }

                    } else {
                        logger.error("九州棋局初始化排行榜失败，赛制类型:{}，赛区类型:{},activityId:{}", entry.getKey(), entry1.getKey(), config.getActivityId());
                    }
                }
            } else if (entry.getKey() == eJiuZhouRoundType.knockout.getValue()) {
                for (Map.Entry<Integer, List<JiuZhouRank>> entry1 : entry.getValue().entrySet()) {
                    CrossJiuZhouRankListModel crossJiuZhouRankListModel = outRoundRankMap.get(entry1.getKey());
                    if (crossJiuZhouRankListModel != null) {
                        crossJiuZhouRankListModel.batchRankChange(entry1.getValue());
                        //特殊处理先 全是insert...
                        for (JiuZhouRank rank:crossJiuZhouRankListModel.getRankList()){
                            rank.setNoneOption();
                        }
                    } else {
                        logger.error("九州棋局初始化排行榜失败，赛制类型:{}，赛区类型:{},activityId:{}", entry.getKey(), entry1.getKey(), config.getActivityId());
                    }
                }
            }
        }
        // 房间的战斗排名
        for (CrossJiuZhouRoomData roomData : roomDataMap.values()) {
            initRoomBattleRankModel(roomData);
        }
        // 人气榜单
        for (JiuZhouRoundConfig roundConfig : config.getRoundConfigMap().values()) {
            if (roundConfig.getType() != eJiuZhouRoundType.knockout.getValue()){
                continue;
            }
            for (eJiuZhouGameZoneType zoneType : eJiuZhouGameZoneType.values()) {
                String popularityRankKey = getPopularityRankKey(zoneType.getValue(), roundConfig.getId());
                CrossJiuZhouRankListModel rankListModel = popularityRankMap.getOrDefault(popularityRankKey, new CrossJiuZhouRankListModel());
                List<JiuZhouRank> ranks = new ArrayList<>();
                for (CrossJiuZhouUserData userData : userDataMap.values()) {
                    // 未开启的轮次不初始化
                    if (userData.getZoneType() != zoneType.getValue() || roundConfig.getId() > userData.getLastRoundId()) {
                        continue;
                    }
                    Long popularityValue = userData.getPopularityMap().getOrDefault(roundConfig.getId(), 0L);
                    List<Long> paramList = new ArrayList<>();
                    paramList.add(userData.getPatronsAbility().longValue());
                    ranks.add(new JiuZhouRank(config.getActivityId(), userData.getUserId(), popularityValue, paramList, nowTime));
                }
                rankListModel.batchRankChange(ranks);
                popularityRankMap.put(popularityRankKey, rankListModel);
            }

        }
    }

    /**
     * 初始化房间榜单
     */
    private void initRoomBattleRankModel(CrossJiuZhouRoomData roomData) {
        long nowTime = System.currentTimeMillis();

        CrossJiuZhouRankListModel rankListModel = roomBattleRankMap.getOrDefault(roomData.getRoomId(), new CrossJiuZhouRankListModel(new JiuZhouRoomBattleRankSort()));
        // value 是存活时间
        // param 是血量#伤害值#伤害更新时间
        List<JiuZhouRank> list = new ArrayList<>();
        for (Long userId : roomData.getRoomUserIdList()) {
            CrossJiuZhouUserData userData = getUserData(userId);
            if (userData == null) {
                continue;
            }
            // param 回合数#血量#伤害值#门客赚钱
            List<Long> paramList = new ArrayList<>();
            if (roomData.getGameData() != null) {
                CrossJiuZhouGameUserData gameUserData = roomData.getGameData().getGameUserData(userId);
                if (gameUserData.getLastGameRound() > 0 && gameUserData.getCurHp() > 0) {
                    paramList.add((long) config.getMaxFightRound());
                } else {
                    paramList.add((long) gameUserData.getLastBattleRound());
                }
                paramList.add((long) gameUserData.getCurHp());
                paramList.add((long) gameUserData.getAttackDamage());
                paramList.add(userData.getPatronsAbility().longValue());
                list.add(new JiuZhouRank(config.getActivityId(), userData.getUserId(), gameUserData.getLastGameRound(), paramList, nowTime));
            } else {
                list.add(new JiuZhouRank(config.getActivityId(), userData.getUserId(), 0, paramList, nowTime));
            }
        }
        rankListModel.batchRankChange(list);
        roomBattleRankMap.put(roomData.getRoomId(), rankListModel);
    }

    public void save() {
        logger.info("九州棋局数据保存,活动id:{}", config.getActivityInfo().getActivityId());
        //保存活动轮次竞猜数据
        for (CrossJiuZhouGuessData guessData : roundGuessDataMap.values()) {
            if (guessData.isInsertOption()){
                JiuZhouBussiness.addCrossJiuZhouGuessData(guessData);
            }else if (guessData.isUpdateOption()){
                JiuZhouBussiness.updateCrossJiuZhouGuessData(guessData);
            }
        }
        //保存活动玩家竞猜数据
        for (CrossJiuZhouUserGuessData userGuessData : userGuessDataMap.values()) {
            if (userGuessData.isInsertOption()){
                JiuZhouBussiness.addCrossJiuZhouUserGuessData(userGuessData);
            }else if (userGuessData.isUpdateOption()){
                JiuZhouBussiness.updateCrossJiuZhouUserGuessData(userGuessData);
            }
        }
        //保存轮次数据
        for (CrossJiuZhouRoundData roundData: roundDataMap.values()) {
            if (roundData.isInsertOption()){
                JiuZhouBussiness.addCrossJiuZhouRoundData(roundData);
            }else if (roundData.isUpdateOption()){
                JiuZhouBussiness.updateCrossJiuZhouRoundData(roundData);
            }
        }
        for (CrossJiuZhouUserData userData : userDataMap.values()) {
            if (userData.isInsertOption()) {
                JiuZhouBussiness.addCrossJiuZhouUserData(userData);
            } else if (userData.isUpdateOption()) {
                JiuZhouBussiness.updateCrossJiuZhouUserData(userData);
            }
        }
        for (Map<Long, List<CrossJiuZhouDeployData>> map : deployDataMap.values()) {
            for (List<CrossJiuZhouDeployData> list : map.values()) {
                for (CrossJiuZhouDeployData deployData : list) {
                    if (deployData.isInsertOption()) {
                        JiuZhouBussiness.addCrossJiuZhouDeployData(deployData);
                    } else if (deployData.isUpdateOption()) {
                        JiuZhouBussiness.updateCrossJiuZhouDeployData(deployData);
                    }
                }
            }
        }
        for (CrossJiuZhouRoomData roomData : roomDataMap.values()) {
            if (roomData.isInsertOption()) {
                JiuZhouBussiness.addCrossJiuZhouRoomData(roomData);
            } else if (roomData.isUpdateOption()) {
                JiuZhouBussiness.updateCrossJiuZhouRoomData(roomData);
            }
        }
        for (List<CrossJiuZhouGameRoundRecordData> list : gameRoundRecordDataMap.values()) {
            for (int i = 0; i < list.size(); i++) {
                CrossJiuZhouGameRoundRecordData recordData = list.get(i);
                if (recordData.isInsertOption()) {
                    JiuZhouBussiness.addCrossJiuZhouGameRoundRecordData(recordData);
                }
            }
        }

        for (CrossJiuZhouRankListModel rankListModel : scoreRoundRankMap.values()) {
            for (JiuZhouRank rank : rankListModel.getRankList()) {
                if (rank.isInsertOption()) {
                    JiuZhouBussiness.addJiuZhouRank(rank);
                } else if (rank.isUpdateOption()) {
                    JiuZhouBussiness.updateJiuZhouRank(rank);
                }
            }
        }

        for (CrossJiuZhouRankListModel rankListModel : outRoundRankMap.values()) {
            for (JiuZhouRank rank : rankListModel.getRankList()) {
                if (rank.isInsertOption()) {
                    JiuZhouBussiness.addJiuZhouRank(rank);
                } else if (rank.isUpdateOption()) {
                    JiuZhouBussiness.updateJiuZhouRank(rank);
                }
            }
        }
    }

    public void debugTask() {
        logger.info("九州棋局debug定时任务,活动id:{}", config.getActivityInfo().getActivityId());
    }


    /**
     * 创建房间
     */
    public CrossJiuZhouRoomData createRoomData(int zone, int round) {
        // 当前轮次
        CrossJiuZhouRoomData roomData = new CrossJiuZhouRoomData();
        roomData.setActivityId(config.getActivityInfo().getActivityId());
        roomData.setZone(zone);
        roomData.setRound(round);
        roomData.setState(eJiuZhouRoomStateType.Init.getValue());
        roomData.setRoomId(maxRoomId.incrementAndGet());
        roomData.setRoomUserIdList(new ArrayList<>());
        roomData.setInsertOption();
        roomDataMap.put(roomData.getRoomId(), roomData);
        addToRoomIdMap(round,roomData.getRoomId());
        return roomData;
    }

    /**
     * 添加房间id到RoomIdMap  Map<roundId,roomId>
     */
    public void addToRoomIdMap(int roundId, int roomId) {
        if (roundRoomIdMap == null){
            roundRoomIdMap = new ConcurrentHashMap<>();
        }
        synchronized (roundRoomIdMap){
            if (!roundRoomIdMap.containsKey(roundId)){
                roundRoomIdMap.put(roundId,new ArrayList<>());
            }
            roundRoomIdMap.get(roundId).add(roomId);
        }
    }

    /**
     * 部署进程
     * 生成房间用户战斗数据
     *
     * @param roomData 房间数据
     */
    public void deployProcess(CrossJiuZhouRoomData roomData) {
        if (roomData == null) {
            return;
        }
        if (roomData.getState() != eJiuZhouRoomStateType.Init.getValue()) {
            // 已生成过，不再处理
            return;
        }

        // 加入房间的时候处理一下榜单
        initRoomBattleRankModel(roomData);

        // 所有用户
        List<Long> allUserList = new ArrayList<>(roomData.getRoomUserIdList());
        // 打乱一下
        Collections.shuffle(allUserList);
        for (int i = 0; i < allUserList.size(); i++) {
            for (int j = i + 1; j < allUserList.size(); j++) {
                // 给个初始值吧，防止出问题
                int skillId1 = 6;
                JiuZhouSkillConfig skillConfig = randomSkill1(i + 1, j + 1);
                if (skillConfig != null) {
                    // 空了就是有问题
                    skillId1 = skillConfig.getId();
                }
                long userId1 = allUserList.get(i);
                long userId2 = allUserList.get(j);
                // 创建布阵信息
                // 我方
                createDeployData(roomData.getRoomId(), userId1, userId2, skillId1);
                // 敌方
                createDeployData(roomData.getRoomId(), userId2, userId1, skillId1);
            }
        }

        // 改下房间状态
        roomData.setState(eJiuZhouRoomStateType.Deploy.getValue());
        // 房间状态同步
        roomStateSyncMsg(roomData);
    }

    /**
     * 房间状态同步
     * @param roomData
     */
    public void roomStateSyncMsg(CrossJiuZhouRoomData roomData) {
        if (roomData == null) {
            return;
        }
        ConcurrentHashSet<Long> userListSync = getRoomUserListSync(roomData.getRoomId());
        for (Long userId : userListSync) {
            JiuZhouProto.JiuZhouRoomStateSyncMsg.Builder syncMsg = JiuZhouProto.JiuZhouRoomStateSyncMsg.newBuilder();
            syncMsg.setRoomId(roomData.getRoomId());
            syncMsg.setState(roomData.getState());
            if (roomData.getRoomUserIdList().contains(userId)) {
                // 参战的
                syncMsg.setType(0);
            } else {
                // 观战的
                syncMsg.setType(1);
            }
            sendPacket(Protocol.U_JIU_ZHOU_ROOM_STATE_SYNC, syncMsg, userId);
        }
    }

    /**
     * 战斗进程
     *
     * @param roomData 房间数据
     */
    public void battleProcess(CrossJiuZhouRoomData roomData) {
        CrossJiuZhouRoundData roundData = roundDataMap.get(roomData.getZone());
        if (roundData == null) {
            return;
        }
        // 需要到开战准备期
        if (roundData.getCurRoundState() != eJiuZhouRoundTime.fight.getValue()) {
            return;
        }

        // 战场匹配进程
        battleMatchProcess(roomData);
        // 选锦囊阶段（士气比拼、棋书生效、推锦囊）
        battleSkillChooseProcess(roomData);
        // 战斗主进程
        battleMainProcess(roomData);
        // 战斗结算
        battleSettleProcess(roomData);
    }

    /**
     * 选锦囊进程
     * 需要推送消息给客户端表现
     *
     * @param roomData 房间数据
     */
    private void battleSkillChooseProcess(CrossJiuZhouRoomData roomData) {
        if (roomData == null) {
            return;
        }
        if (roomData.getState() == eJiuZhouRoomStateType.End.getValue()) {
            // 房间已结算
            return;
        }
        CrossJiuZhouGameData gameData = roomData.getGameData();
        if (gameData == null) {
            return;
        }
        // 仅在战前准备期能切换到锦囊期
        if (roomData.getState() != eJiuZhouRoomStateType.Battle.getValue() || roomData.getGameData().getStageType() != eJiuZhouGameStageType.Prepare.getValue()) {
            return;
        }
        long nowTime = System.currentTimeMillis();
        if (nowTime < roomData.getGameData().getNextStageTime()) {
            // 时间未到
            return;
        }
        //  生成锦囊数据
        for (CrossJiuZhouGameBattleData battleData : roomData.getGameData().getBattleDataList()) {
            // 士气比拼, 获取一下士气技能id
            int skillId4 = getSkillId4();
            if (battleData.getBattleUser1().getSilverBuff().compareTo(battleData.getBattleUser2().getSilverBuff()) > 0) {
                battleData.getBattleUser1().getSkillIds().add(skillId4);
//                logger.info("房间id：{},士气比拼,玩家：{}，获胜",roomData.getRoomId(),battleData.getBattleUser1().getUserId());
            } else if (battleData.getBattleUser1().getSilverBuff().compareTo(battleData.getBattleUser2().getSilverBuff()) < 0) {
                battleData.getBattleUser2().getSkillIds().add(skillId4);
//                logger.info("房间id：{},士气比拼,玩家：{}，获胜",roomData.getRoomId(),battleData.getBattleUser2().getUserId());
            } else {
                // 相等了,双方都不加成
//                logger.info("房间id：{},士气相等", roomData.getRoomId());
            }

            // 开始记录
            battleData.startRecord();
            // 加个表现步骤
            battleData.addNewStep();

            // 玩家1技能生效,战前技能效果
            battleData.addSkillEffect(skillEffect(battleData, eJiuZhouSkillEffectType.BeforeChoose, true), 1);

            // 玩家2技能生效,战前技能效果
            battleData.addSkillEffect(skillEffect(battleData, eJiuZhouSkillEffectType.BeforeChoose, false), 1);

            // 棋书、士气生效后再锁定一下最强门客
            battleData.getBattleUser1().putAttribute(MaxAbilityPatronsId, getBattleMaxAbilityPatronsId(battleData.getBattleUser1()));
            // 棋书、士气生效后再锁定一下最弱门客
            battleData.getBattleUser1().putAttribute(MinAbilityPatronsId, getBattleMinAbilityPatronsId(battleData.getBattleUser1()));

            // 棋书、士气生效后再锁定一下最强门客
            battleData.getBattleUser2().putAttribute(MaxAbilityPatronsId, getBattleMaxAbilityPatronsId(battleData.getBattleUser2()));
            // 棋书、士气生效后再锁定一下最弱门客
            battleData.getBattleUser2().putAttribute(MinAbilityPatronsId, getBattleMinAbilityPatronsId(battleData.getBattleUser2()));

            // 玩家1添加待选择的锦囊
            rollChooseSkillId3(roomData, battleData.getBattleUser1());

            // 玩家2添加待选择的锦囊
            rollChooseSkillId3(roomData, battleData.getBattleUser2());


            // 结束记录
            battleData.endRecord();
        }
        roomData.getGameData().setStageType(eJiuZhouGameStageType.ChooseSkillId3.getValue());
        roomData.getGameData().setNextStageTime(nowTime + config.getChooseSilkTime() * DateHelper.SECOND_MILLIONS);

        // 推送
        battleSyncMsg(roomData, 1);
    }

    /**
     * 测试的待选锦囊
     */
    private List<Integer> debugSkillId3 = new ArrayList<>();

    /**
     * roll 待选锦囊
     *
     * @param roomData
     * @param battleUser
     */
    public void rollChooseSkillId3(CrossJiuZhouRoomData roomData, CrossJiuZhouGameBattleUserData battleUser) {
        if (roomData == null || roomData.getGameData() == null || battleUser == null) {
            return;
        }
        CrossJiuZhouGameUserData gameUserData = roomData.getGameData().getGameUserData(battleUser.getUserId());
        if (gameUserData == null) {
            return;
        }
        // roll之前清空下
        battleUser.getSkillId3List().clear();
        List<Integer> debugChooseList = new ArrayList<>(debugSkillId3);
        // 需要排除的
        List<Integer> excludeSkillIdList = new ArrayList<>();
        // 过滤掉需要排除的技能
        for (JiuZhouSkillConfig skillConfig : config.getSkillConfigMap().values()) {
            if (skillConfig.getType() != eJiuZhouBigSkillType.Skill3.getValue()) {
                continue;
            }
            // 不限制
            if (skillConfig.getUseNum() == -1) {
                continue;
            }
            // 大于使用限制
            if (gameUserData.getUseSkillCnt().getOrDefault(skillConfig.getId(), 0) >= skillConfig.getUseNum()) {
                excludeSkillIdList.add(skillConfig.getId());
            }
        }

        // 开始随机
        for (int i = 0; i < 10000; i++) {
            if (battleUser.getSkillId3List().size() >= 3) {
                break;
            }
            if (!debugChooseList.isEmpty()) {
                Integer skillId = debugChooseList.remove(0);
                battleUser.getSkillId3List().add(skillId);
                excludeSkillIdList.add(skillId);
            } else {
                JiuZhouSkillConfig skillConfig = randomSkillExclude(eJiuZhouBigSkillType.Skill3.getValue(), excludeSkillIdList);
                if (skillConfig == null) {
                    // 找不到了就跳出，别卡死了
                    continue;
                }
                battleUser.getSkillId3List().add(skillConfig.getId());
                excludeSkillIdList.add(skillConfig.getId());
            }
        }
    }

    /**
     * 技能生效
     * 属性加成
     */
    private List<CrossJiuZhouSkillEffect> skillEffect(CrossJiuZhouGameBattleData battleData, eJiuZhouSkillEffectType effectType, boolean isUser1) {
        CrossJiuZhouGameBattleUserData myBattleUserData = null;
        CrossJiuZhouGameBattleUserData targetBattleUserData = null;
        if (isUser1) {
            myBattleUserData = battleData.getBattleUser1();
            targetBattleUserData = battleData.getBattleUser2();
        } else {
            myBattleUserData = battleData.getBattleUser2();
            targetBattleUserData = battleData.getBattleUser1();
        }
        // 回合数
        int battleRound = battleData.getBattleRound();
        List<CrossJiuZhouSkillEffect> skillEffectList = new ArrayList<>();
        if (myBattleUserData.getSkillIds().isEmpty()) {
            // 没有技能不处理
            return skillEffectList;
        }
        for (Integer skillId : myBattleUserData.getSkillIds()) {
            JiuZhouSkillConfig skillConfig = config.getSkillConfig(skillId);
            if (skillConfig == null) {
                continue;
            }
            eJiuZhouSkillType eJiuZhouSkillType = com.yanqu.road.entity.enums.activity.jiuzhou.eJiuZhouSkillType.forSkillId(skillId);
            if (eJiuZhouSkillType == null) {
                logger.error("未知技能类型:{}", skillId);
                continue;
            }
            // 过滤一下生效类型
            if (!eJiuZhouSkillType.getEffectTypeList().contains(effectType)) {
                continue;
            }
            // 技能效果
            CrossJiuZhouSkillEffect skillEffect = new CrossJiuZhouSkillEffect();

            skillEffect.setUserId(getBattleUserId(myBattleUserData));
            skillEffect.setSkillId(skillId);
//            logger.info("玩家：{}，技能生效,技能id:{}，技能名称:{}", myBattleUserData.getUserId(), skillId, eJiuZhouSkillType.getName());
            List<Integer> paramList = StringUtils.stringToIntegerList(skillConfig.getSkillParam(), "\\|");

            // 门客变更数据
            List<Integer> myPatronsChangeList = new ArrayList<>();
            List<Integer> targetPatronsChangeList = new ArrayList<>();

            // 是否触发
            boolean isTrigger = true;
            switch (eJiuZhouSkillType) {
                case Skill1:
                case Skill2:
                case Skill3:
                case Skill4:
                case Skill5:
                case Skill6:
                    // 加概率权重  参数:加成职业|加成百分比
                    for (CrossJiuZhouGamePatronsData patronsData : myBattleUserData.getPatronsDataMap().values()) {
                        PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(patronsData.getPatronsId());
                        if (patronsInfo == null) {
                            continue;
                        }
                        // 判断一下职业
                        if (paramList.get(0) != 0 && paramList.get(0) != patronsInfo.getOccupation()) {
                            continue;
                        }
                        myPatronsChangeList.add(patronsInfo.getId());
                        // 加成算上
                        patronsData.setAddWeightRadio(patronsData.getAddWeightRadio() + paramList.get(1));
                    }
                    break;
                case Skill13:
                    // 获取对方前3门客随机一个，直接
                    CrossJiuZhouGamePatronsData topAbilityPatrons = getBattleTopAbilityPatrons(targetBattleUserData, randomHelper.next(1, paramList.get(0) + 1));
                    if (topAbilityPatrons != null) {
                        isTrigger = true;
                        // 开偷,偷基础
                        CrossJiuZhouGamePatronsData myPatronsData = myBattleUserData.getPatronsData(myBattleUserData.getAttribute(MinAbilityPatronsId));
                        myPatronsData.setBaseAbility(new BigInteger(topAbilityPatrons.getBaseAbility().toString()));

                        // 门客变化
                        myPatronsChangeList.add(myBattleUserData.getAttribute(MinAbilityPatronsId));
                    } else {
                        isTrigger = false;
                    }
                    break;
                case Skill117:
                    // 门客战斗胜利造成伤害+{0}%
                    if (effectType == eJiuZhouSkillEffectType.BeforeAttack) {
                        if (myBattleUserData.getAttribute(BattleResult, false)) {
                            myBattleUserData.putAttribute(AttackDamageValue, myBattleUserData.getAttribute(AttackDamageValue, 0) + paramList.get(0));
                        }
                        // 只生效不触发
                        isTrigger = false;
                    }
                    if (effectType == eJiuZhouSkillEffectType.AfterAttack) {
                        if (myBattleUserData.getAttribute(BattleResult, false)) {
                            isTrigger = true;
                        } else {
                            // 战败不触发
                            isTrigger = false;
                        }
                    }

                    break;
                case Skill14:
                case Skill109:
                    // 玩家战力最强的门客，出战概率额外+{0}%
                    CrossJiuZhouGamePatronsData patronsData1 = myBattleUserData.getPatronsData(myBattleUserData.getAttribute(MaxAbilityPatronsId, 0));
                    if (patronsData1 != null) {
                        myPatronsChangeList.add(patronsData1.getPatronsId());
                        // 加成算上
                        patronsData1.setAddWeightRadio(patronsData1.getAddWeightRadio() + paramList.get(0));
                    }
                    break;
                case Skill101:
                case Skill102:
                case Skill103:
                case Skill104:
                case Skill105:
                    // 赚钱加成 参数:加成职业|加成百分比
                    for (CrossJiuZhouGamePatronsData patronsData : myBattleUserData.getPatronsDataMap().values()) {
                        PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(patronsData.getPatronsId());
                        if (patronsInfo == null) {
                            continue;
                        }
                        // 判断一下职业
                        if (paramList.get(0) != 0 && paramList.get(0) != patronsInfo.getOccupation()) {
                            continue;
                        }
                        myPatronsChangeList.add(patronsInfo.getId());
                        // 加成算上
                        patronsData.setAddRadio(patronsData.getAddRadio() + paramList.get(1));
                    }
                    break;
                case Skill12:
                case Skill401:
                    // 赚钱加成 参数:加成百分比
                    for (CrossJiuZhouGamePatronsData patronsData : myBattleUserData.getPatronsDataMap().values()) {
                        PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(patronsData.getPatronsId());
                        if (patronsInfo == null) {
                            continue;
                        }
                        myPatronsChangeList.add(patronsInfo.getId());
                        // 加成算上
                        patronsData.setAddRadio(patronsData.getAddRadio() + paramList.get(0));
                    }
                    break;
                case Skill11:
                case Skill113:
                    // 护盾值
                    extraHpChange(battleData, myBattleUserData, paramList.get(0));
                    break;
                case Skill112:
                    // 回血
                    hpChange(battleData, myBattleUserData, paramList.get(0), false, 2);
                    break;
                case Skill106:
                    // 每回合增加{1}%门客赚钱
                    Integer round = myBattleUserData.getSkillEffectRoundMap().getOrDefault(skillConfig.getId(), 0);
                    if (paramList.get(0) != 0 && battleRound - round >= paramList.get(0)) {
                        for (CrossJiuZhouGamePatronsData patronsData : myBattleUserData.getPatronsDataMap().values()) {
                            PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(patronsData.getPatronsId());
                            if (patronsInfo == null) {
                                continue;
                            }
                            myPatronsChangeList.add(patronsInfo.getId());
                            // 加成算上
                            patronsData.setAddRadio(patronsData.getAddRadio() + paramList.get(1));
                        }
                    } else {
                        // 回合冷却，不触发
                        isTrigger = false;
                    }
                    myBattleUserData.getSkillEffectRoundMap().put(skillConfig.getId(), battleRound);
                    break;
                case Skill107:
                    // 最弱门客偷窃对方随机门客{0}%赚钱
                    CrossJiuZhouGamePatronsData patronsData1071 = myBattleUserData.getPatronsData(myBattleUserData.getAttribute(MinAbilityPatronsId));
                    if (patronsData1071 != null) {
                        List<Integer> targetPatronsIdList = new ArrayList<>(targetBattleUserData.getPatronsDataMap().keySet());
                        if (!targetPatronsIdList.isEmpty()) {
                            Integer targetPatronsId = (Integer) randomHelper.randomList(targetPatronsIdList);
                            CrossJiuZhouGamePatronsData patronsData1072 = targetBattleUserData.getPatronsData(targetPatronsId);
                            if (patronsData1072 != null) {
                                patronsData1072.setAddRadio(patronsData1072.getAddRadio() - paramList.get(0));
                                patronsData1071.setAddValue(patronsData1071.getAddValue().add(BigInteger.valueOf(BigDecimal.valueOf(patronsData1072.getBaseAbility().longValue()).multiply(BigDecimal.valueOf(paramList.get(0))).divide(BigDecimal.valueOf(100), 0, RoundingMode.UP).longValue())));
                                myPatronsChangeList.add(patronsData1071.getPatronsId());
                                targetPatronsChangeList.add(patronsData1072.getPatronsId());
                            }
                        }
                    }
                    break;
                case Skill108:
                    // 最弱的门客不出战
                    List<Integer> banPatronsIds = myBattleUserData.getAttribute(BanPatronsIds, new ArrayList<>());
                    banPatronsIds.add(myBattleUserData.getAttribute(MinAbilityPatronsId));
                    myBattleUserData.putAttribute(BanPatronsIds, banPatronsIds);
                    break;
//                case Skill109:
//                    // 最强门客出战概率+{0}%  实时的最强门客
//                    myBattleUserData.putAttribute(MaxAbilityPatronsBattleUp, myBattleUserData.getAttribute(MaxAbilityPatronsBattleUp, 0) + paramList.get(0));
//                    break;
                case Skill110:
                    // 每次派出目前赚钱最高的门客，但最多只造成{0}伤害
                    if (effectType == eJiuZhouSkillEffectType.BeforeRound) {
                        // 获取当前实时最高的门客
                        int battleMaxAbilityPatronsId = getBattleMaxAbilityPatronsId(myBattleUserData);
                        myBattleUserData.putAttribute(BattlePatronsId, battleMaxAbilityPatronsId);
                        if (myBattleUserData.getAttribute(Skill110LockPatronsId, 0) != battleMaxAbilityPatronsId) {
                            // 可触发
                            isTrigger = true;
                            myBattleUserData.putAttribute(Skill110LockPatronsId, battleMaxAbilityPatronsId);
                        } else {
                            // 不可触发
                            isTrigger = false;
                        }
                    }
                    // 伤害上限
                    if (effectType == eJiuZhouSkillEffectType.BeforeAttack) {
                        myBattleUserData.putAttribute(AttackDamageLimit, paramList.get(0));
                        // 只生效不触发
                        isTrigger = false;
                    }
                    if (effectType == eJiuZhouSkillEffectType.AfterAttack) {
                        // 胜利了生效
                        if (myBattleUserData.getAttribute(BattleResult, false)) {
                            isTrigger = true;
                        } else {
                            isTrigger = false;
                        }
                    }
                    break;
                case Skill111:
                    //  使敌方最强门客最多出战{0}次
                    if (effectType == eJiuZhouSkillEffectType.BeforeBattle) {
                        targetBattleUserData.putAttribute(MaxAbilityPatronsBattleLimit, paramList.get(0));
                        // 这里不触发
                        isTrigger = false;
                    }
                    // 判断是否锁掉
                    if (effectType == eJiuZhouSkillEffectType.AfterRound) {
                        List<Integer> banPatronsIds1 = targetBattleUserData.getAttribute(BanPatronsIds, new ArrayList<>());
                        if (targetBattleUserData.getAttributeMap().containsKey(MaxAbilityPatronsBattleLimit)
                                && targetBattleUserData.getPatronsData(targetBattleUserData.getAttribute(MaxAbilityPatronsId, 0)).getBattleTimes() >= targetBattleUserData.getAttribute(MaxAbilityPatronsBattleLimit, 0)
                                && !banPatronsIds1.contains(targetBattleUserData.getAttribute(MaxAbilityPatronsId, 0))) {
                            // 锁掉敌人最强门客
                            banPatronsIds1.add(targetBattleUserData.getAttribute(MaxAbilityPatronsId, 0));
                            targetBattleUserData.putAttribute(BanPatronsIds, banPatronsIds1);
                            isTrigger = true;
                        } else {
                            isTrigger = false;
                        }
                    }
                    break;
                case Skill114:
                    // 门客战斗胜利恢复{0}生命
                    if (myBattleUserData.getAttribute(BattleResult, false)) {
                        hpChange(battleData, myBattleUserData, paramList.get(0), false, 2);
                    } else {
                        // 战败不触发
                        isTrigger = false;
                    }
                    break;
                case Skill115:
                    // 使对方最强门客伤害减半
                    if (effectType == eJiuZhouSkillEffectType.BeforeAttack) {
                        if (targetBattleUserData.getAttribute(BattleResult, false)
                                && targetBattleUserData.getAttribute(MaxAbilityPatronsId, 0) != 0
                                && Objects.equals(targetBattleUserData.getAttribute(BattlePatronsId, 0), targetBattleUserData.getAttribute(MaxAbilityPatronsId, 0))) {
                            targetBattleUserData.putAttribute(AttackDamageRadio, targetBattleUserData.getAttribute(AttackDamageRadio, 0) - paramList.get(0));
                        }
                        // 只生效不触发
                        isTrigger = false;
                    }
                    if (effectType == eJiuZhouSkillEffectType.AfterAttack) {
                        if (targetBattleUserData.getAttribute(BattleResult, false)
                                && targetBattleUserData.getAttribute(MaxAbilityPatronsId, 0) != 0
                                && Objects.equals(targetBattleUserData.getAttribute(BattlePatronsId, 0), targetBattleUserData.getAttribute(MaxAbilityPatronsId, 0))) {
                            // 触发
                            isTrigger = true;
                        } else {
                            // 不触发
                            isTrigger = false;
                        }
                    }

                    break;
                case Skill116:
                    // 本场对阵血量最低降至{0}   锁血
                    // 比较特殊,战前需要设置锁血值，，但是被打触发血线才算触发锁血
                    if (effectType == eJiuZhouSkillEffectType.BeforeBattle) {
                        myBattleUserData.putAttribute(BattleLockHp, paramList.get(0));
                        // 这里不算触发
                        isTrigger = false;
                    }
                    if (effectType == eJiuZhouSkillEffectType.HpChange) {
                        if (myBattleUserData.getAttributeMap().containsKey(TriggerLockHp)) {
                            isTrigger = true;
                        } else {
                            isTrigger = false;
                        }
                    }
                    break;
                case Skill118:
                    // 最弱门客出战概率+{0}%，造成{1}/100倍伤害
                    if (effectType == eJiuZhouSkillEffectType.BeforeBattle) {
                        CrossJiuZhouGamePatronsData patronsData3 = myBattleUserData.getPatronsData(myBattleUserData.getAttribute(MinAbilityPatronsId, 0));
                        if (patronsData3 != null) {
                            patronsData3.setAddWeightRadio(patronsData3.getAddWeightRadio() + paramList.get(0));
                        }
                    }
                    if (effectType == eJiuZhouSkillEffectType.BeforeAttack) {
                        if (myBattleUserData.getAttribute(BattleResult, false) && Objects.equals(myBattleUserData.getAttribute(BattlePatronsId, 0), myBattleUserData.getAttribute(MinAbilityPatronsId, 0))) {
                            // 伤害加成
                            myBattleUserData.putAttribute(AttackDamageRadio, myBattleUserData.getAttribute(AttackDamageRadio, 0) + paramList.get(1));
                        }
                        // 仅生效不触发
                        isTrigger = false;
                    }
                    if (effectType == eJiuZhouSkillEffectType.AfterAttack) {
                        if (myBattleUserData.getAttribute(BattleResult, false) && Objects.equals(myBattleUserData.getAttribute(BattlePatronsId, 0), myBattleUserData.getAttribute(MinAbilityPatronsId, 0))) {
                            // 触发
                            isTrigger = true;
                        } else {
                            // 战败不触发
                            isTrigger = false;
                        }
                    }
                    break;
                case Skill119:
                    // 若我方门客赚钱高于对方{0}/100倍，则造成{1}/100倍伤害(改成是对方的多少倍)
                    double myAbility = myBattleUserData.getPatronsData(myBattleUserData.getAttribute(BattlePatronsId, 0)).getAbility().doubleValue();
                    double targetAbility = targetBattleUserData.getPatronsData(targetBattleUserData.getAttribute(BattlePatronsId, 0)).getAbility().doubleValue();
                    if (effectType == eJiuZhouSkillEffectType.BeforeAttack) {
                        if (myAbility / targetAbility > paramList.get(0) / 100.0) {
                            myBattleUserData.putAttribute(AttackDamageRadio, myBattleUserData.getAttribute(AttackDamageRadio, 0) + paramList.get(1));
                        }
                        // 仅生效不触发
                        isTrigger = false;
                    }
                    if (effectType == eJiuZhouSkillEffectType.AfterAttack) {
                        if (myAbility / targetAbility > paramList.get(0) / 100.0) {
                            isTrigger = true;
                        } else {
                            // 不满足触发条件
                            isTrigger = false;
                        }
                    }
                    break;
                case Skill120:
                    // 自身血量-{0}(最低降至1)，获得{1}%吸血
                    if (effectType == eJiuZhouSkillEffectType.BeforeBattle) {
                        // 计算可降低的血量  当前-掉血>=1
                        hpChange(battleData, myBattleUserData, -Math.min(myBattleUserData.getHp() - 1, paramList.get(0)), false, 1);
                    }
                    if (effectType == eJiuZhouSkillEffectType.HpChange) {
                        if (myBattleUserData.getAttribute(BattleResult, false)) {
                            isTrigger = true;
                            myBattleUserData.putAttribute(SuckBloodRadio, myBattleUserData.getAttribute(SuckBloodRadio, 0) + paramList.get(1));
                        } else {
                            // 不满足触发条件
                            isTrigger = false;
                        }
                    }
                    break;
                case Skill121:
                    // 自身血量-{0}(最低降至1)，门客赚钱+{1}%
                    hpChange(battleData, myBattleUserData, -Math.min(myBattleUserData.getHp() - 1, paramList.get(0)), false, 1);
                    for (CrossJiuZhouGamePatronsData patronsData : myBattleUserData.getPatronsDataMap().values()) {
                        PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(patronsData.getPatronsId());
                        if (patronsInfo == null) {
                            continue;
                        }
                        myPatronsChangeList.add(patronsInfo.getId());
                        // 加成算上
                        patronsData.setAddRadio(patronsData.getAddRadio() + paramList.get(1));
                    }
                    break;
                case Skill122:
                    // 听天由命
                    int addRadio = (int) randomHelper.randomList(paramList);
                    for (CrossJiuZhouGamePatronsData patronsData : myBattleUserData.getPatronsDataMap().values()) {
                        PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(patronsData.getPatronsId());
                        if (patronsInfo == null) {
                            continue;
                        }
                        myPatronsChangeList.add(patronsInfo.getId());
                        // 加成算上
                        patronsData.setAddRadio(patronsData.getAddRadio() + addRadio);
                    }
                    break;
                case Skill15:
                    // 被攻击时基础伤害最高为{0}，该伤害可被其他效果加成。
                    // 伤害上限
                    if (effectType == eJiuZhouSkillEffectType.BeforeChoose) {
                        // 给对方加buff
                        targetBattleUserData.putAttribute(AttackBaseDamageLimit, paramList.get(0));
                        // 只生效不触发
                        isTrigger = false;
                    }
                    if (effectType == eJiuZhouSkillEffectType.BeforeAttack) {
                        // 胜利了生效
                        if (targetBattleUserData.getAttribute(BattleResult, false) && targetBattleUserData.getAttribute(BattlePatronsDamage, 0) > paramList.get(0)) {
                            isTrigger = true;
                        } else {
                            isTrigger = false;
                        }
                    }

                    break;
                default:
                    break;
            }

            if (isTrigger) {
                // 技能加成后参数
                afterSkillEffect(myBattleUserData, targetBattleUserData, skillEffect, myPatronsChangeList, targetPatronsChangeList, effectType);

                skillEffectList.add(skillEffect);
            }

        }
        return skillEffectList;
    }

    /**
     * 计算门客伤害值
     * 这里很乱，客户端没有自己算，需要两套算法
     */
    public int calcPatronsDamage(int gameRound, CrossJiuZhouGameBattleUserData battleUserData) {
//        // 计算一个应该造成的伤害（不含场次增伤）
//        int clientDamage = BigDecimal.valueOf(battleUserData.getAttribute(BattlePatronsDamage, 0))
//                .add(BigDecimal.valueOf(battleUserData.getAttribute(AttackDamageValue, 0)))
//                .multiply(BigDecimal.valueOf(100 + battleUserData.getAttribute(AttackDamageRadio, 0))).divide(BigDecimal.valueOf(100), 8, RoundingMode.UP)
//                .setScale(0, RoundingMode.UP)
//                .intValue();
//        if (battleUserData.getAttributeMap().containsKey(AttackDamageLimit)) {
//            clientDamage = Math.min(clientDamage, battleUserData.getAttribute(AttackDamageLimit, 0));
//        }
//        battleUserData.putAttribute(BattlePatronsDamageClient, clientDamage);

        // 基础伤害
        int baseDamage = battleUserData.getAttribute(BattlePatronsDamage, 0);

        // 造成的伤害(实际的，含场次增伤)
        int damage = BigDecimal.valueOf(baseDamage)
                .add(BigDecimal.valueOf(battleUserData.getAttribute(AttackDamageValue, 0)))
                .multiply(BigDecimal.valueOf(100 + battleUserData.getAttribute(AttackDamageRadio, 0))).divide(BigDecimal.valueOf(100), 8, RoundingMode.UP)
                .multiply(BigDecimal.valueOf(calcFightMultiple(gameRound))).divide(BigDecimal.valueOf(100), 8, RoundingMode.UP)
                .setScale(0, RoundingMode.UP)
                .intValue();
        if (battleUserData.getAttributeMap().containsKey(AttackDamageLimit)) {
            // 伤害限制
            damage = Math.min(damage, battleUserData.getAttribute(AttackDamageLimit, 0));
        }
        // 记录下本次可造成的伤害值
        battleUserData.putAttribute(BattlePatronsDamage, damage);
        return damage;
    }

    /**
     * 技能效果加成后
     *
     * @param myBattleUserData
     * @param targetBattleUserData
     * @param skillEffect
     * @param myPatronsChangeList
     * @param targetPatronsChangeList
     */
    private void afterSkillEffect(CrossJiuZhouGameBattleUserData myBattleUserData, CrossJiuZhouGameBattleUserData targetBattleUserData, CrossJiuZhouSkillEffect skillEffect, List<Integer> myPatronsChangeList, List<Integer> targetPatronsChangeList, eJiuZhouSkillEffectType effectType) {
        if (skillEffect == null) {
            return;
        }
        int skillId = skillEffect.getSkillId();
        JiuZhouSkillConfig skillConfig = config.getSkillConfig(skillId);
        if (skillConfig == null) {
            return;
        }
        eJiuZhouSkillType eJiuZhouSkillType = com.yanqu.road.entity.enums.activity.jiuzhou.eJiuZhouSkillType.forSkillId(skillId);
        if (eJiuZhouSkillType == null) {
            logger.error("未知技能类型:{}", skillId);
            return;
        }
        List<Long> skillArgs = skillEffect.getSkillArgs();
        switch (eJiuZhouSkillType) {
            case Skill401:
            case Skill12:
            case Skill101:
            case Skill102:
            case Skill103:
            case Skill104:
            case Skill105:
            case Skill106:
            case Skill122:
            case Skill13:
                // 门客id1、门客加成后的战力1、...门客id5、门客加成后的战力5
                for (Integer patronsId : myPatronsChangeList) {
                    CrossJiuZhouGamePatronsData patronsData = myBattleUserData.getPatronsData(patronsId);
                    if (patronsData == null) {
                        continue;
                    }
                    skillArgs.add((long) patronsData.getPatronsId());
                    skillArgs.add(patronsData.getAbility().longValue());
                }
                break;
            case Skill11:
            case Skill113:
                // 11、113：玩家最终护盾值
                skillArgs.add((long) myBattleUserData.getExtraHp());
                break;
            case Skill114:
            case Skill116:
                // 13、114、116：战斗结果那边用，暂时不用参数值
                break;
            case Skill14:
            case Skill109:
                // 14、109：门客id、门客出战增加的概率，目前暂时不用表现，可以不发
                break;
            case Skill107:
                // 107：我方门客id、我方门客加成后的战力、对方门客id、对方门客加成后的战力      (暂时有两个  我，敌)
                for (Integer patronsId : myPatronsChangeList) {
                    CrossJiuZhouGamePatronsData patronsData = myBattleUserData.getPatronsData(patronsId);
                    if (patronsData == null) {
                        continue;
                    }
                    skillArgs.add((long) patronsData.getPatronsId());
                    skillArgs.add(patronsData.getAbility().longValue());
                }
                // 敌方
                for (Integer patronsId : targetPatronsChangeList) {
                    CrossJiuZhouGamePatronsData patronsData = targetBattleUserData.getPatronsData(patronsId);
                    if (patronsData == null) {
                        continue;
                    }
                    skillArgs.add((long) patronsData.getPatronsId());
                    skillArgs.add(patronsData.getAbility().longValue());
                }
                break;
            case Skill108:
                // 108：不出战的门客id
                List<Integer> banPatronsIds108 = myBattleUserData.getAttribute(BanPatronsIds, new ArrayList<>());
                for (Integer patronsId : banPatronsIds108) {
                    skillArgs.add(Long.valueOf(patronsId));
                }
                break;
            case Skill111:
                // 101：敌方不出战的门客id
                List<Integer> banPatronsIds111 = targetBattleUserData.getAttribute(BanPatronsIds, new ArrayList<>());
                for (Integer patronsId : banPatronsIds111) {
                    skillArgs.add(Long.valueOf(patronsId));
                }
                break;
            case Skill110:
                // 110、我方赚钱最高门客id
                // 每次派出目前赚钱最高的门客，但最多只造成{0}伤害
                if (effectType == eJiuZhouSkillEffectType.BeforeRound) {
                    skillArgs.add(0L);
                    skillArgs.add(Long.valueOf(myBattleUserData.getAttribute(BattlePatronsId, 0)));
                }
                // 伤害上限
                if (effectType == eJiuZhouSkillEffectType.AfterAttack) {
                    skillArgs.add(1L);
                    skillArgs.add(Long.valueOf(myBattleUserData.getAttribute(BattlePatronsId, 0)));
                    skillArgs.add(Long.valueOf(myBattleUserData.getAttribute(AttackDamageLimit, 0)));
                }
                break;
            case Skill112:
                // 112：血量最终值
                skillArgs.add((long) myBattleUserData.getHp());
                break;
            case Skill115:
                // 115：敌方门客id，敌方门客改变后的伤害
                if (targetBattleUserData.getAttribute(BattleResult, false) && Objects.equals(targetBattleUserData.getAttribute(BattlePatronsId), targetBattleUserData.getAttribute(MaxAbilityPatronsId))) {
                    skillArgs.add(Long.valueOf(targetBattleUserData.getAttribute(BattlePatronsId, 0)));
                    skillArgs.add(Long.valueOf(targetBattleUserData.getAttribute(BattlePatronsDamage, 0)));
                }
                break;
            case Skill15:
                // 被攻击时基础伤害最高为{0}，该伤害可被其他效果加成。
                if (targetBattleUserData.getAttribute(BattleResult, false)) {
                    skillArgs.add(Long.valueOf(targetBattleUserData.getAttribute(BattlePatronsDamage, 0)));
                }
                break;
            case Skill117:
            case Skill119:
                // 117、119：我方门客id，我方门客改变后的伤害
                skillArgs.add(Long.valueOf(myBattleUserData.getAttribute(BattlePatronsId, 0)));
                skillArgs.add(Long.valueOf(myBattleUserData.getAttribute(BattlePatronsDamage, 0)));
                break;
            case Skill118:
                // 118: 类型 0，我方门客id  类型 1，我方门客id，我方门客改变后的伤害
                if (effectType == eJiuZhouSkillEffectType.BeforeBattle) {
                    skillArgs.add(0L);
                    skillArgs.add(Long.valueOf(myBattleUserData.getAttribute(MinAbilityPatronsId, 0)));
                } else if (effectType == eJiuZhouSkillEffectType.AfterAttack) {
                    // 只有最弱的出战了且战胜了才造成3倍
                    if (myBattleUserData.getAttribute(BattleResult, false) && Objects.equals(myBattleUserData.getAttribute(BattlePatronsId, 0), myBattleUserData.getAttribute(MinAbilityPatronsId, 0))) {
                        skillArgs.add(1L);
                        skillArgs.add(Long.valueOf(myBattleUserData.getAttribute(BattlePatronsId, 0)));
                        skillArgs.add(Long.valueOf(myBattleUserData.getAttribute(BattlePatronsDamage, 0)));
                    }
                }
                break;
            case Skill120:
                // 120：类型（0、自身减血，1、吸血）、血量最终值（类型为0的时候才有）
                if (effectType == eJiuZhouSkillEffectType.BeforeBattle) {
                    skillArgs.add(0L);
                    skillArgs.add((long) myBattleUserData.getHp());
                }
                if (effectType == eJiuZhouSkillEffectType.HpChange) {
                    skillArgs.add(1L);
                }
                break;
            case Skill121:
                // 121：血量最终值、 门客id1、门客加成后的战力1、...门客id5、门客加成后的战力5
                skillArgs.add((long) myBattleUserData.getHp());
                for (Integer patronsId : myPatronsChangeList) {
                    CrossJiuZhouGamePatronsData patronsData = myBattleUserData.getPatronsData(patronsId);
                    if (patronsData == null) {
                        continue;
                    }
                    skillArgs.add((long) patronsData.getPatronsId());
                    skillArgs.add(patronsData.getAbility().longValue());
                }
                break;
            default:
                break;
        }

    }

    /**
     * 战斗表现同步
     *
     * @param roomData 房间数据
     * @param syncType 同步类型
     */
    public void battleSyncMsg(CrossJiuZhouRoomData roomData, int syncType) {
        if (roomData == null || roomData.getGameData() == null) {
            return;
        }
        CrossJiuZhouGameData gameData = roomData.getGameData();
        JiuZhouProto.JiuZhouBattleSyncMsg.Builder syncMsg = JiuZhouProto.JiuZhouBattleSyncMsg.newBuilder();
        syncMsg.setRoomId(roomData.getRoomId());
        syncMsg.setType(syncType);
        for (CrossJiuZhouGameBattleData battleData : gameData.getBattleDataList()) {
            CrossJiuZhouGameBattleShowData showData = battleData.getShowData();
            if (showData == null) {
                continue;
            }
            JiuZhouProto.JiuZhouGameBattleShowDataMsg.Builder showTemp = JiuZhouProto.JiuZhouGameBattleShowDataMsg.newBuilder();
            showTemp.setBattleData(parseJiuZhouGameBattleDataMsg(showData.getOriginBattleData()));
            // 表现步骤
            for (CrossJiuZhouGameStepData stepData : showData.getStepDataList()) {
                showTemp.addStepData(JiuZhouPb.parseJiuZhouGameStepDataMsg(stepData));
            }
            showTemp.setResultBattleData(parseJiuZhouGameBattleDataMsg(showData.getResultBattleData()));
            syncMsg.addShowData(showTemp);
        }
        syncMsg.setGameRound(gameData.getGameRound());
        syncMsg.setStageType(gameData.getStageType());
        syncMsg.setNextStageTime(gameData.getNextStageTime());

        // 房间用户数据
        List<JiuZhouRank> rankList = getRoomRankList(roomData.getRoomId());
        int rankNum = 0;
        for (JiuZhouRank rank : rankList) {
            if (rank == null) {
                continue;
            }
            rankNum++;
            CrossJiuZhouGameUserData gameUserData = roomData.getGameData().getGameUserData(rank.getUserId());
            if (gameUserData == null) {
                continue;
            }
            CrossJiuZhouUserData userData = getUserData(gameUserData.getUserId());
            if (userData == null) {
                continue;
            }
            JiuZhouProto.JiuZhouGameUserDataMsg.Builder gameUserMsg = JiuZhouProto.JiuZhouGameUserDataMsg.newBuilder();
            gameUserMsg.setUserId(userData.getUserId());
            // 这里不给userbaseinfo   推送变更，不需要这个，东西太多了
//            CrossUserBaseInfo crossUserBaseInfo = getCrossUserBaseInfo(userData.getUserId());
//            if (crossUserBaseInfo != null) {
//                gameUserMsg.setUserBaseInfo(PlayerBasePb.parsePlayerBaseTempMsg(crossUserBaseInfo.getUserBaseInfo()));
//            }
            // 门客信息
            for (CrossJiuZhouPatronsData patronsData : userData.getPatronsDataMap().values()) {
                gameUserMsg.addPatronsData(JiuZhouPb.parsePatronsSimpleTempMsg(patronsData));
            }
            // 当前血量
            gameUserMsg.setHp(gameUserData.getCurHp());
            // 当前排名
            gameUserMsg.setRank(rankNum);
            syncMsg.addUserData(gameUserMsg);
        }
        sendPacketToAll(Protocol.U_JIU_ZHOU_BATTLE_SYNC, syncMsg, getRoomUserListSync(roomData.getRoomId()));
    }

    /**
     * 主战斗逻辑
     *
     * @param roomData 房间信息
     */
    public void battleMainProcess(CrossJiuZhouRoomData roomData) {
        if (roomData == null) {
            return;
        }
        if (roomData.getState() == eJiuZhouRoomStateType.End.getValue()) {
            // 房间已结算
            return;
        }
        CrossJiuZhouGameData gameData = roomData.getGameData();
        if (gameData == null) {
            return;
        }
        if (roomData.getState() != eJiuZhouRoomStateType.Battle.getValue() || gameData.getStageType() != eJiuZhouGameStageType.ChooseSkillId3.getValue()) {
            return;
        }
        long nowTime = System.currentTimeMillis();
        if (nowTime < gameData.getNextStageTime()) {
            // 时间未到
            return;
        }
        // 先设置状态，避免战斗卡了死循环了
        gameData.setStageType(eJiuZhouGameStageType.Battle.getValue());
        // 90s战斗时间
        gameData.setNextStageTime(nowTime + config.getFightCd() * DateHelper.SECOND_MILLIONS);

        // 需要同步的用户列表
        Set<Long> needSyncDieUserSet = new HashSet<>();

        //  战斗
        for (CrossJiuZhouGameBattleData battleData : gameData.getBattleDataList()) {

            // 记录下战前的血量
            battleData.getBattleUser1().putAttribute(BeforeBattleHp, battleData.getBattleUser1().getHp());
            battleData.getBattleUser2().putAttribute(BeforeBattleHp, battleData.getBattleUser2().getHp());

            // 调试模式,人机选择锦囊
            if (debug) {
                if (battleData.getBattleUser1().getChooseSkillId3() == 0) {
                    battleData.getBattleUser1().setChooseSkillId3((Integer) randomHelper.randomList(battleData.getBattleUser1().getSkillId3List()));
                }
                if (battleData.getBattleUser2().getChooseSkillId3() == 0) {
                    battleData.getBattleUser2().setChooseSkillId3((Integer) randomHelper.randomList(battleData.getBattleUser2().getSkillId3List()));
                }
            }

            // 战前准备,添加锦囊(镜像不生效锦囊)
            if (!battleData.getBattleUser1().isMirror() && battleData.getBattleUser1().getChooseSkillId3() != 0) {
                battleData.getBattleUser1().getSkillIds().add(battleData.getBattleUser1().getChooseSkillId3());
            }
            if (!battleData.getBattleUser2().isMirror() && battleData.getBattleUser2().getChooseSkillId3() != 0) {
                battleData.getBattleUser2().getSkillIds().add(battleData.getBattleUser2().getChooseSkillId3());
            }

            // 使用技能计数一下
            CrossJiuZhouGameUserData gameUserData1 = roomData.getGameData().getGameUserData(battleData.getBattleUser1().getUserId());
            if (gameUserData1 != null) {
                for (Integer skillId : battleData.getBattleUser1().getSkillIds()) {
                    gameUserData1.getUseSkillCnt().put(skillId, gameUserData1.getUseSkillCnt().getOrDefault(skillId, 0) + 1);
                }
            }
            CrossJiuZhouGameUserData gameUserData2 = roomData.getGameData().getGameUserData(battleData.getBattleUser2().getUserId());
            if (gameUserData2 != null) {
                for (Integer skillId : battleData.getBattleUser2().getSkillIds()) {
                    gameUserData2.getUseSkillCnt().put(skillId, gameUserData2.getUseSkillCnt().getOrDefault(skillId, 0) + 1);
                }
            }

            // 开始记录
            battleData.startRecord();
            // 加个表现步骤
            battleData.addNewStep();

            // 战前技能效果生效
            battleData.addSkillEffect(skillEffect(battleData, eJiuZhouSkillEffectType.BeforeBattle, true), 1);
            // 战前技能效果生效
            battleData.addSkillEffect(skillEffect(battleData, eJiuZhouSkillEffectType.BeforeBattle, false), 1);

            // 开始战斗
            while (!battleData.isFinish()) {
                int oldHp1 = battleData.getBattleUser1().getHp();
                int oldHp2 = battleData.getBattleUser2().getHp();
                // 战斗
                fight(battleData);

                // 战斗后玩家处理一下
                afterFight(roomData, battleData.getBattleUser1(), battleData.getBattleRound());
                afterFight(roomData, battleData.getBattleUser2(), battleData.getBattleRound());

                int newHp1 = battleData.getBattleUser1().getHp();
                int newHp2 = battleData.getBattleUser2().getHp();

                if (!battleData.getBattleUser1().isMirror() && newHp1 <= 0 && oldHp1 > 0) {
                    needSyncDieUserSet.add(battleData.getBattleUser1().getUserId());
                }
                if (!battleData.getBattleUser2().isMirror() && newHp2 <= 0 && oldHp2 > 0) {
                    needSyncDieUserSet.add(battleData.getBattleUser2().getUserId());
                }

                // 每回合战斗需要记录日志
                CrossAutoLogMgr.add(new LogJiuZhouBattle(
                        config.getActivityId(),
                        roomData.getRound(),
                        roomData.getRoomId(),
                        roomData.getGameData().getGameRound(),
                        battleData.getBattleRound(),
                        battleData.getBattleUser1().getUserId(),
                        battleData.getBattleUser2().getUserId(),
                        battleData.getBattleUser1().getChooseSkillId3(),
                        battleData.getBattleUser2().getChooseSkillId3(),
                        battleData.getBattleUser1().getAttribute(BattlePatronsId, 0),
                        battleData.getBattleUser2().getAttribute(BattlePatronsId, 0),
                        newHp1 - oldHp1,
                        newHp2 - oldHp2,
                        nowTime
                ));
            }
            // 结束记录
            battleData.endRecord();

            // 添加战报记录
            CrossJiuZhouGameRoundRecordData recordData = new CrossJiuZhouGameRoundRecordData();
            recordData.setActivityId(config.getActivityId());
            recordData.setRoomId(roomData.getRoomId());
            recordData.setRound(roomData.getRound());
            recordData.setGameRound(gameData.getGameRound());
            // 玩家1
            recordData.setUser1(createCrossJiuZhouGameRoundRecordUserData(battleData.getBattleUser1()));
            // 玩家2
            recordData.setUser2(createCrossJiuZhouGameRoundRecordUserData(battleData.getBattleUser2()));
            recordData.setInsertOption();

            List<CrossJiuZhouGameRoundRecordData> gameRoundRecordDataList = gameRoundRecordDataMap.getOrDefault(roomData.getRoomId(), new ArrayList<>());
            gameRoundRecordDataList.add(recordData);
            gameRoundRecordDataMap.put(roomData.getRoomId(), gameRoundRecordDataList);
        }
        // 更新一下排行榜
        CrossJiuZhouRankListModel rankListModel = roomBattleRankMap.getOrDefault(roomData.getRoomId(), new CrossJiuZhouRankListModel(new JiuZhouRoomBattleRankSort()));
        List<JiuZhouRank> ranks = new ArrayList<>();
        for (CrossJiuZhouGameUserData gameUserData : roomData.getGameData().getGameUserDataMap().values()) {
            CrossJiuZhouUserData userData = getUserData(gameUserData.getUserId());
            if (userData == null) {
                continue;
            }
            // param 回合数#血量#伤害值#门客赚钱
            List<Long> paramList = new ArrayList<>();
            if (gameUserData.getLastGameRound() > 0 && gameUserData.getCurHp() > 0) {
                paramList.add((long) config.getMaxFightRound());
            } else {
                paramList.add((long) gameUserData.getLastBattleRound());
            }
            paramList.add((long) gameUserData.getCurHp());
            paramList.add((long) gameUserData.getAttackDamage());
            paramList.add(userData.getPatronsAbility().longValue());
            ranks.add(new JiuZhouRank(config.getActivityId(), gameUserData.getUserId(), gameUserData.getLastGameRound(), paramList, nowTime));
        }
        // 批量更新
        rankListModel.batchRankChange(ranks);
        roomBattleRankMap.put(roomData.getRoomId(), rankListModel);
        //  战斗推送
        battleSyncMsg(roomData, 2);


        // 淘汰推送
        for (Long dieUserId : needSyncDieUserSet) {
            dieOutSyncMsg(roomData.getRoomId(), dieUserId);
        }

        // 检测游戏
        checkGameOver(roomData);
    }

    private CrossJiuZhouGameRoundRecordUserData createCrossJiuZhouGameRoundRecordUserData(CrossJiuZhouGameBattleUserData battleUserData) {
        CrossJiuZhouGameRoundRecordUserData recordUserData = new CrossJiuZhouGameRoundRecordUserData();
        recordUserData.setUserId(battleUserData.getUserId());
        recordUserData.setUserType(battleUserData.getUserType());
        recordUserData.setBeforeHp(battleUserData.getAttribute(BeforeBattleHp, 0));
        recordUserData.setAfterHp(battleUserData.getHp());
        // 记录下玩家1门客数据(先不记录)
//        for (Integer patronsId : battleUserData.getPatronsDataMap().keySet()) {
//            CrossJiuZhouPatronsData patronsData = getUserData(battleUserData.getUserId()).getPatronsDataMap().get(patronsId);
//            if (patronsData == null) {
//                continue;
//            }
//            recordUserData.getPatronsDataList().add(patronsData.clone());
//        }
        return recordUserData;
    }

    private void afterFight(CrossJiuZhouRoomData roomData, CrossJiuZhouGameBattleUserData battleUserData, int battleRound) {
        if (roomData == null || roomData.getGameData() == null) {
            return;
        }
        if (battleUserData.getUserType() == eJiuZhouBattleUserType.Mirror.getValue()) {
            return;
        }
        CrossJiuZhouGameUserData gameUserData = roomData.getGameData().getGameUserData(battleUserData.getUserId());
        if (gameUserData == null) {
            return;
        }
        CrossJiuZhouUserData userData = getUserData(battleUserData.getUserId());
        if (userData == null) {
            return;
        }


        // 当前剩余血量
        gameUserData.setCurHp(battleUserData.getHp());

        // 没死才加回合数
        if (gameUserData.getCurHp() > 0) {
            gameUserData.setLastBattleRound(battleRound);
        }

        // 累计伤害
        gameUserData.setAttackDamage(gameUserData.getAttackDamage() + battleUserData.getAttribute(BattlePatronsDamageResult, 0));
    }

    /**
     * 回合前处理
     */
    private void beforeRoundHandler(CrossJiuZhouGameBattleData battleData) {
        // 回合数先加一下
        battleData.setBattleRound(battleData.getBattleRound() + 1);

        // 每回合加个表现步骤
        battleData.addNewStep();
        // 回合之前需要处理下
        resetBattleAttribute(battleData.getBattleUser1());
        resetBattleAttribute(battleData.getBattleUser2());

        // 回合前锦囊生效
        battleData.addSkillEffect(skillEffect(battleData, eJiuZhouSkillEffectType.BeforeRound, true), 1);

        battleData.addSkillEffect(skillEffect(battleData, eJiuZhouSkillEffectType.BeforeRound, false), 1);

        // 上面的生效效果要当成单独一步，每次战斗当成新的一步
        battleData.addNewStep();
    }


    /**
     * 战斗
     *
     * @param battleData 战场数据
     */
    private void fight(CrossJiuZhouGameBattleData battleData) {
        CrossJiuZhouRoomData roomData = getRoomData(battleData.getRoomId());
        if (roomData == null) {
            return;
        }

        beforeRoundHandler(battleData);

//        logger.info("战场:开始战斗,回合:{}，玩家1:{},玩家2:{}", battleData.getBattleRound(), battleData.getBattleUser1().getUserId(), battleData.getBattleUser2().getUserId());

        // 玩家1 拿个门客
        CrossJiuZhouGamePatronsData patronsData1 = rollPatrons(battleData.getBattleRound(), battleData.getBattleUser1());
        // 玩家2 拿个门客
        CrossJiuZhouGamePatronsData patronsData2 = rollPatrons(battleData.getBattleRound(), battleData.getBattleUser2());
        if (patronsData1 == null || patronsData2 == null) {
            // 做个容错，别卡死了
            if (patronsData1 == null) {
                logger.error("门客出战异常,玩家:{},门客信息:{}", battleData.getBattleUser1().getUserId(), JSON.toJSONString(battleData.getBattleUser1().getPatronsDataMap()));
            }
            if (patronsData2 == null) {
                logger.error("门客出战异常,玩家:{},门客信息:{}", battleData.getBattleUser2().getUserId(), JSON.toJSONString(battleData.getBattleUser2().getPatronsDataMap()));
            }
            battleData.setFinish(true);
            return;
        }
        // 记录下出战的门客
        // 增加出战次数
        patronsData1.setBattleTimes(patronsData1.getBattleTimes() + 1);
        battleData.getBattleUser1().putAttribute(BattlePatronsId, patronsData1.getPatronsId());

        patronsData2.setBattleTimes(patronsData2.getBattleTimes() + 1);
        battleData.getBattleUser2().putAttribute(BattlePatronsId, patronsData2.getPatronsId());


//        logger.info("门客1战力：{}", patronsData1.getAbility());
//        logger.info("门客2战力：{}", patronsData2.getAbility());

        // 门客剩余血量
        long leftHp1 = Math.max(0, patronsData1.getAbility().subtract(patronsData2.getAbility()).longValue());
        long leftHp2 = Math.max(0, patronsData2.getAbility().subtract(patronsData1.getAbility()).longValue());

        // 门客剩余血量比拼
        if (leftHp1 > leftHp2) {
//            logger.info("玩家1门客强");
            battleData.getBattleUser1().putAttribute(BattleResult, true);
            // 门客剩余血量千分比
            int hpRadio = BigDecimal.valueOf(leftHp1).divide(BigDecimal.valueOf(patronsData1.getAbility().longValue()), 8, RoundingMode.UP).multiply(BigDecimal.valueOf(1000)).setScale(0, RoundingMode.UP).intValue();

            // 计算基础伤害
            int baseDamage = calcAttackDamage(roomData.getRound(), hpRadio);
            if (battleData.getBattleUser1().getAttributeMap().containsKey(AttackBaseDamageLimit)) {
                baseDamage = Math.min(baseDamage, battleData.getBattleUser1().getAttribute(AttackBaseDamageLimit, 0));
            }
            battleData.getBattleUser1().putAttribute(BattlePatronsDamage, baseDamage);
        } else if (leftHp1 < leftHp2) {
//            logger.info("玩家2门客强");
            battleData.getBattleUser2().putAttribute(BattleResult, true);
            // 门客剩余血量千分比
            int hpRadio = BigDecimal.valueOf(leftHp2).divide(BigDecimal.valueOf(patronsData2.getAbility().longValue()), 8, RoundingMode.UP).multiply(BigDecimal.valueOf(1000)).setScale(0, RoundingMode.UP).intValue();

            // 计算基础伤害
            int baseDamage = calcAttackDamage(roomData.getRound(), hpRadio);
            if (battleData.getBattleUser2().getAttributeMap().containsKey(AttackBaseDamageLimit)) {
                baseDamage = Math.min(baseDamage, battleData.getBattleUser2().getAttribute(AttackBaseDamageLimit, 0));
            }
            battleData.getBattleUser2().putAttribute(BattlePatronsDamage, baseDamage);
        } else {
//            logger.info("门客战力相等，不造成任何伤害");
        }

        // 造成伤害之前需要计算技能效果
        battleData.addSkillEffect(skillEffect(battleData, eJiuZhouSkillEffectType.BeforeAttack, true), 2);

        battleData.addSkillEffect(skillEffect(battleData, eJiuZhouSkillEffectType.BeforeAttack, false), 2);

        // 造成伤害
        if (battleData.getBattleUser1().getAttribute(BattleResult, false)) {
            attack(roomData.getGameData().getGameRound(), battleData, battleData.getBattleUser1(), battleData.getBattleUser2());
        } else if (battleData.getBattleUser2().getAttribute(BattleResult, false)) {
            attack(roomData.getGameData().getGameRound(), battleData, battleData.getBattleUser2(), battleData.getBattleUser1());
        } else {
            // 不造成伤害
        }
        // 造成伤害之后给客户端的效果
        battleData.addSkillEffect(skillEffect(battleData, eJiuZhouSkillEffectType.AfterAttack, true), 2);

        battleData.addSkillEffect(skillEffect(battleData, eJiuZhouSkillEffectType.AfterAttack, false), 2);

        // 血量变化触发
        battleData.addSkillEffect(skillEffect(battleData, eJiuZhouSkillEffectType.HpChange, true), 3);
        battleData.addSkillEffect(skillEffect(battleData, eJiuZhouSkillEffectType.HpChange, false), 3);

        // 血量变更
        afterAttack(battleData, battleData.getBattleUser1());
        afterAttack(battleData, battleData.getBattleUser2());

        // 回合结束后触发个技能
        battleData.addSkillEffect(skillEffect(battleData, eJiuZhouSkillEffectType.AfterRound, true), 4);

        battleData.addSkillEffect(skillEffect(battleData, eJiuZhouSkillEffectType.AfterRound, false), 4);

        // 记录下门客战斗结果
        battleData.addPatronsFightResult(new CrossJiuZhouPatronsFightResult(getBattleUserId(battleData.getBattleUser1()), patronsData1.getPatronsId(), leftHp1, battleData.getBattleUser1().getAttribute(BattlePatronsDamage, 0)));
        battleData.addPatronsFightResult(new CrossJiuZhouPatronsFightResult(getBattleUserId(battleData.getBattleUser2()), patronsData2.getPatronsId(), leftHp2, battleData.getBattleUser2().getAttribute(BattlePatronsDamage, 0)));

        // 有一方血量为0，或者达到最大回合数 直接结束游戏,退出循环
        if (battleData.getBattleUser1().getHp() <= 0 || battleData.getBattleUser2().getHp() <= 0 || battleData.getBattleRound() >= config.getMaxFightRound()) {
            battleData.setFinish(true);
        }
    }

    private void afterAttack(CrossJiuZhouGameBattleData battleData,CrossJiuZhouGameBattleUserData battleUserData) {
        // 计算吸血
        if (battleUserData.getAttributeMap().containsKey(SuckBloodRadio)) {
            int suckHp = BigDecimal.valueOf(battleUserData.getAttribute(BattlePatronsDamageResult, 0)).multiply(BigDecimal.valueOf(battleUserData.getAttribute(SuckBloodRadio, 0))).divide(BigDecimal.valueOf(100), 0, RoundingMode.UP).intValue();
            // 血量变化
            hpChange(battleData, battleUserData, suckHp, false, 2);
        }
    }

    /**
     * 血量变化
     *
     * @param battleData
     * @param battleUserData
     * @param offsetHp
     * @param type 1 扣血 2加血
     */
    private void hpChange(CrossJiuZhouGameBattleData battleData, CrossJiuZhouGameBattleUserData battleUserData, int offsetHp, boolean isLockHp, int type) {
        battleUserData.hpChange(offsetHp);
        if (isLockHp) {
            addHpResult(battleData, battleUserData, 3);
        } else if (type == 2) {
            // 加血
            addHpResult(battleData, battleUserData, 2);
        } else if (type == 1) {
            // 扣血
            addHpResult(battleData, battleUserData, 1);
        }
    }

    /**
     * 护盾值变化
     * @param battleData
     * @param battleUserData
     * @param offsetExtraHp
     */
    private void extraHpChange(CrossJiuZhouGameBattleData battleData, CrossJiuZhouGameBattleUserData battleUserData, int offsetExtraHp) {
        battleUserData.extraHpChange(offsetExtraHp);
        addHpResult(battleData, battleUserData, 4);
    }

    /**
     * 获取本局游戏的战绩
     */
    public List<CrossJiuZhouGameRoundRecordData> getGameRoundRecordList(int roomId, long userId) {
        List<CrossJiuZhouGameRoundRecordData> list = new ArrayList<>();
        List<CrossJiuZhouGameRoundRecordData> roundRecordDataList = gameRoundRecordDataMap.getOrDefault(roomId, new ArrayList<>());
        for (CrossJiuZhouGameRoundRecordData recordData : roundRecordDataList) {
            if ((recordData.getUser1().getUserType() == eJiuZhouBattleUserType.Normal.getValue() && recordData.getUser1().getUserId() == userId)
                    || (recordData.getUser2().getUserType() == eJiuZhouBattleUserType.Normal.getValue() && recordData.getUser2().getUserId() == userId)) {
                list.add(recordData);
            }
        }
        return list;
    }

    /**
     * 淘汰推送
     */
    public void dieOutSyncMsg(int roomId, long userId) {
        CrossJiuZhouRoomData roomData = getRoomData(roomId);
        if (roomData == null) {
            return;
        }
        JiuZhouProto.JiuZhouDieOutSyncMsg.Builder syncMsg = JiuZhouProto.JiuZhouDieOutSyncMsg.newBuilder();
        syncMsg.setUserId(userId);
        syncMsg.setRoomId(roomId);
        List<CrossJiuZhouGameRoundRecordData> roundRecordList = getGameRoundRecordList(roomId, userId);
        for (CrossJiuZhouGameRoundRecordData recordData : roundRecordList) {
            syncMsg.addRecord(parseJiuZhouGameRoundRecordMsg(recordData));
        }
        // 推送一下当前排名
        List<JiuZhouRank> rankList = getRoomRankList(roomId);
        // 根据排名推送一下
        int rankNum = 0;
        for (JiuZhouRank rank : rankList) {
            if (rank == null) {
                continue;
            }
            rankNum++;
            long targetUserId = rank.getUserId();
            CrossJiuZhouUserData userData = getUserData(targetUserId);
            if (userData == null) {
                continue;
            }
            CrossUserBaseInfo crossUserBaseInfo = getCrossUserBaseInfo(targetUserId);
            if (crossUserBaseInfo == null) {
                continue;
            }
            CrossJiuZhouGameUserData gameUserData = roomData.getGameData().getGameUserData(targetUserId);
            if (gameUserData == null) {
                continue;
            }
            // 结算信息
            JiuZhouProto.JiuZhouSettleUserMsg.Builder userTemp = JiuZhouProto.JiuZhouSettleUserMsg.newBuilder();
            userTemp.setUserId(targetUserId);
            userTemp.setUserBaseInfo(PlayerBasePb.parsePlayerBaseTempMsg(crossUserBaseInfo.getUserBaseInfo()));
            // 临时排名
            userTemp.setRank(rankNum);
            userTemp.setAttackDamage(gameUserData.getAttackDamage());
            userTemp.setLastGameRound(gameUserData.getLastGameRound());
            userTemp.setLastBattleRound(gameUserData.getLastBattleRound());
            userTemp.setHp(gameUserData.getCurHp());
            syncMsg.addUser(userTemp);
            if (targetUserId == userId) {
                // 排名
                syncMsg.setRank(rankNum);
            }
        }

        sendPacket(Protocol.U_JIU_ZHOU_DIE_OUT_SYNC, syncMsg, userId);
    }

    private JiuZhouProto.JiuZhouGameRoundRecordUserMsg.Builder parseJiuZhouGameRoundRecordUserMsg(CrossJiuZhouGameRoundRecordUserData recordUserData) {
        if (recordUserData == null) {
            return null;
        }
        JiuZhouProto.JiuZhouGameRoundRecordUserMsg.Builder builder = JiuZhouProto.JiuZhouGameRoundRecordUserMsg.newBuilder();
        builder.setUserId(recordUserData.getUserId());
        CrossUserBaseInfo crossUserBaseInfo = getCrossUserBaseInfo(recordUserData.getUserId());
        if (crossUserBaseInfo == null) {
            return null;
        }
        builder.setUserBaseInfo(PlayerBasePb.parsePlayerBaseTempMsg(crossUserBaseInfo.getUserBaseInfo()));
        builder.setBeforeHp(recordUserData.getBeforeHp());
        builder.setAfterHp(recordUserData.getAfterHp());
        // 门客数据 (暂时不给)
        for (CrossJiuZhouPatronsData patronsData : recordUserData.getPatronsDataList()) {
            builder.addPatronsData(JiuZhouPb.parsePatronsSimpleTempMsg(patronsData));
        }
        builder.setUserType(recordUserData.getUserType());
        return builder;
    }

    private JiuZhouProto.JiuZhouGameRoundRecordMsg.Builder parseJiuZhouGameRoundRecordMsg(CrossJiuZhouGameRoundRecordData recordData) {
        if (recordData == null) {
            return null;
        }
        JiuZhouProto.JiuZhouGameRoundRecordMsg.Builder builder = JiuZhouProto.JiuZhouGameRoundRecordMsg.newBuilder();
        builder.setGameRound(recordData.getGameRound());
        builder.setUser1(parseJiuZhouGameRoundRecordUserMsg(recordData.getUser1()));
        builder.setUser2(parseJiuZhouGameRoundRecordUserMsg(recordData.getUser2()));
        return builder;

    }

    /**
     * 获取实际的用户id
     */
    private long getBattleUserId(CrossJiuZhouGameBattleUserData battleUserData) {
        if (battleUserData == null) {
            return 0;
        }
        if (battleUserData.getUserType() == eJiuZhouBattleUserType.Mirror.getValue()) {
            return battleUserData.getMirrorUserId();
        }
        return battleUserData.getUserId();
    }

    /**
     * 造成伤害
     *
     * @param gameRound
     * @param battleData 战场数据
     */
    private void attack(int gameRound, CrossJiuZhouGameBattleData battleData, CrossJiuZhouGameBattleUserData myBattleUserData, CrossJiuZhouGameBattleUserData targetBattleUserData) {
        // 玩家1对玩家2造成伤害
        int attackHp = calcAttackDamage(gameRound, myBattleUserData, targetBattleUserData);

        // 记录一下伤害，后面用到
//        logger.info("玩家1门客强,对玩家2造成伤害，伤害值：{}", attackHp);

        // 需要扣除的血量
        int needSubHp = attackHp;
        if (needSubHp > 0 && targetBattleUserData.getExtraHp() > 0) {
            // 有盾优先扣盾
            // 先算出应该扣多少盾
            int needSubExtraHp = Math.min(needSubHp, targetBattleUserData.getExtraHp());
            extraHpChange(battleData, targetBattleUserData, -needSubExtraHp);
            // 计算还剩多少伤害需要扣
            needSubHp = Math.max(0, needSubHp - needSubExtraHp);

//            addHpResult(battleData, targetBattleUserData, 4);
        }
        if (needSubHp >= 0) {
            // 判断下是否触发锁血
            boolean isLockHp = false;
            if (targetBattleUserData.getAttributeMap().containsKey(BattleLockHp) && targetBattleUserData.getHp() - needSubHp <= targetBattleUserData.getAttribute(BattleLockHp, 0)) {
                isLockHp = true;
                // 计算下锁血下最大能造成的伤害 当前-x >= 上限   x<=当前-上限
                needSubHp = Math.min(needSubHp, Math.max(0, targetBattleUserData.getHp() - targetBattleUserData.getAttribute(BattleLockHp, 0)));
                targetBattleUserData.putAttribute(TriggerLockHp, 1);
            }

            myBattleUserData.putAttribute(BattlePatronsDamageResult, needSubHp);

            // 血量变化
            hpChange(battleData, targetBattleUserData, -needSubHp, isLockHp, 1);
        }
    }

    /**
     * 添加血量变化
     */
    public void addHpResult(CrossJiuZhouGameBattleData battleData, CrossJiuZhouGameBattleUserData battleUserData, int type) {
        CrossJiuZhouHpResult hpResult = new CrossJiuZhouHpResult();
        hpResult.setType(type);
        hpResult.setUserId(getBattleUserId(battleUserData));
        hpResult.setHp(battleUserData.getHp());
        hpResult.setExtraHp(battleUserData.getExtraHp());
        battleData.addHpResult(hpResult);
    }

    /**
     * 攻击前属性处理
     */
    private void resetBattleAttribute(CrossJiuZhouGameBattleUserData battleUser) {
        if (battleUser == null) {
            return;
        }
        Map<eJiuZhouAttributeEnum, Object> attributeMap = battleUser.getAttributeMap();

        attributeMap.remove(SuckBloodRadio);
        attributeMap.remove(AttackDamageLimit);
        attributeMap.remove(AttackDamageRadio);
        attributeMap.remove(AttackDamageValue);
        attributeMap.remove(BattlePatronsId);
        attributeMap.remove(BattleResult);
        attributeMap.remove(BattlePatronsDamage);
//        attributeMap.remove(MaxAbilityPatronsBattleUp);
        attributeMap.remove(BattlePatronsDamageResult);
        attributeMap.remove(TriggerLockHp);
//        attributeMap.remove(BattlePatronsDamageClient);
    }

    /**
     * 计算造成的伤害(实际伤害)
     *
     * @param gameRound
     * @param myBattleUser     我的战斗数据
     * @param targetBattleUser 敌方战斗数据
     */
    private int calcAttackDamage(int gameRound, CrossJiuZhouGameBattleUserData myBattleUser, CrossJiuZhouGameBattleUserData targetBattleUser) {
        // 计算伤害
        int attackDamage = calcPatronsDamage(gameRound, myBattleUser);
//        // 敌人有锁血
//        if (targetBattleUser.getAttributeMap().containsKey(BattleLockHp)) {
//            attackDamage = Math.min(attackDamage, targetBattleUser.getHp() - targetBattleUser.getAttribute(BattleLockHp));
//        }
//        attackDamage = Math.min(attackDamage, targetBattleUser.getHp());

        return attackDamage;
//        return randomHelper.next(1, 100);
    }

    /**
     * 随机一个门客出战
     *
     * @param battleRound
     * @param battleUser  用户信息
     */
    public CrossJiuZhouGamePatronsData rollPatrons(int battleRound, CrossJiuZhouGameBattleUserData battleUser) {
        Map<Integer, Integer> rollMap = new HashMap<>();
        // 已指定门客
        if (battleUser.getAttributeMap().containsKey(BattlePatronsId)) {
            return battleUser.getPatronsData(battleUser.getAttribute(BattlePatronsId));
        }
        // 最后一回合保底机制
        if (battleRound == config.getMaxFightRound()) {
            // 当前最强的门客id
            int curMaxPatronsId = getBattleMaxAbilityPatronsId(battleUser);
            // 判断下有没有出战过(没出战必定出战)
            if (battleUser.getPatronsData(curMaxPatronsId).getBattleTimes() <= 0) {
                return battleUser.getPatronsData(curMaxPatronsId);
            }
        }
        // 禁用的门客ids
        List<Integer> banPatronsIds = battleUser.getAttribute(BanPatronsIds, new ArrayList<>());
        for (CrossJiuZhouGamePatronsData patronsData : battleUser.getPatronsDataMap().values()) {
            // 判断下是否有禁用的
            if (banPatronsIds.contains(patronsData.getPatronsId())) {
                continue;
            }
            // 算一下门客权重
            int weight = BigDecimal.valueOf(config.getPatronsRollWeight())
                    .multiply(BigDecimal.valueOf(100 + patronsData.getAddWeightRadio()).divide(BigDecimal.valueOf(100), 0, RoundingMode.UP))
                    .intValue();

//            if (battleUser.getAttributeMap().containsKey(MaxAbilityPatronsBattleUp) && battleUser.getAttribute(MaxAbilityPatronsId, 0) == patronsData.getPatronsId()) {
//                // 最强门客概率up
//                weight += BigDecimal.valueOf(config.getPatronsRollWeight())
//                        .multiply(BigDecimal.valueOf(battleUser.getAttribute(MaxAbilityPatronsBattleUp, 0)).divide(BigDecimal.valueOf(100), 0, RoundingMode.UP))
//                        .intValue();
//            }

            rollMap.put(patronsData.getPatronsId(), weight);
        }
        return battleUser.getPatronsDataMap().get(WeightHelper.getRandomValue(rollMap));
    }

    /**
     * 战斗结算
     *
     * @param roomData 房间数据
     */
    public void battleSettleProcess(CrossJiuZhouRoomData roomData) {
        if (roomData == null) {
            return;
        }
        if (roomData.getState() == eJiuZhouRoomStateType.End.getValue()) {
            // 房间已结算
            return;
        }
        CrossJiuZhouGameData gameData = roomData.getGameData();
        if (gameData == null) {
            return;
        }
        if (roomData.getState() != eJiuZhouRoomStateType.Battle.getValue() || roomData.getGameData().getStageType() != eJiuZhouGameStageType.Battle.getValue()) {
            return;
        }
        long nowTime = System.currentTimeMillis();
        if (nowTime < roomData.getGameData().getNextStageTime()) {
            // 时间未到
            return;
        }

        // 战场未结束继续匹配
        roomData.getGameData().setStageType(eJiuZhouGameStageType.Prepare.getValue());
    }

    /**
     * 获取房间实时排名
     * 获取时都要先排个序，防止出异常了
     */
    public  List<JiuZhouRank> getRoomRankList(int roomId) {
        List<JiuZhouRank> rankList = new ArrayList<>();
        CrossJiuZhouRankListModel rankListModel = roomBattleRankMap.get(roomId);
        if (rankListModel != null) {
            rankList = rankListModel.getRankList();
        }
        return rankList;
    }

    /**
     * 游戏结束处理
     * @param roomData 房间数据
     */
    private void checkGameOver(CrossJiuZhouRoomData roomData) {
        JiuZhouRoundConfig roundConfig = getJiuZhouRoundConfigById(roomData.getRound());
        if (roundConfig == null) {
            return;
        }
        long nowTime = System.currentTimeMillis();
        // 人数大于1且未达到最大回合数且未达到战斗结束时间  不处理
        if (roomALiveUserList(roomData).size() > 1 && roomData.getGameData().getGameRound() < config.getMaxGameRound()
                && nowTime < getTimeStampAfterBegin(roundConfig.getTimeList().get(3))) {
            return;
        }
        roomData.getGameData().setStageType(eJiuZhouGameStageType.End.getValue());
        // 房间结束了,先改状态
        roomData.setState(eJiuZhouRoomStateType.End.getValue());

        // 能获得奖励的声援对象名次
        int supportRankNum = config.getSupportRankNum();

        // 结算一下
        List<JiuZhouRank> rankList = getRoomRankList(roomData.getRoomId());
        int rankNum = 0;

        List<JiuZhouRank> settleRanks = new ArrayList<>();
        for (JiuZhouRank rank : rankList) {
            rankNum++;
            long userId = rank.getUserId();
            CrossJiuZhouUserData userData = getUserData(userId);
            if (userData == null) {
                continue;
            }
            CrossJiuZhouGameUserData gameUserData = roomData.getGameData().getGameUserData(userId);
            if (gameUserData == null) {
                continue;
            }
            CrossJiuZhouUserRoundRecordData record = new CrossJiuZhouUserRoundRecordData();
            record.setRound(roomData.getRound());
            // 战斗伤害
            record.setAttackDamage(gameUserData.getAttackDamage());
            // 战斗积分
            long battleScore = getBattleScore(roomData.getRound(), rankNum);
            record.setScore(battleScore);
            // 奖励
            Property battleReward = getBattleReward(roomData.getRound(), roomData.getZone(), rankNum);
            record.setRankReward(battleReward);
            // 记录下房间排名
            record.setRoomRank(rankNum);
            userData.getRecordDataMap().put(roomData.getRound(), record);

            List<Long> paramList = new ArrayList<>();
            paramList.add((long) userData.getLastRoundId());
            paramList.add(getTotalAttackDamage(userData, roundConfig.getType()));
            paramList.add(userData.getPatronsAbility().longValue());
            settleRanks.add(new JiuZhouRank(config.getActivityId(), userId, roundConfig.getType(), userData.getZoneType(), nowTime, getTotalScore(userData, roundConfig.getType()), paramList));

            // 记录下结算日志
            CrossAutoLogMgr.add(new LogJiuZhouBattleSettle(
                    config.getActivityId(),
                    roomData.getRound(),
                    roomData.getRoomId(),
                    roomData.getGameData().getGameRound(),
                    rank.getUserId(),
                    rankNum,
                    gameUserData.getCurHp(),
                    gameUserData.getAttackDamage(),
                    battleScore,
                    PropertyHelper.parsePropertyToString(battleReward),
                    nowTime
            ));
            // 决赛才有的
            if (roundConfig.getType() == eJiuZhouRoundType.knockout.getValue()) {
                // 获得声援奖励
                if (rankNum <= supportRankNum) {
                    // 发送声援奖励
                    sendSupportReward(roomData.getZone(), roomData.getRound(), userId, rankNum, true);
                } else {
                    // 发送声援失败奖励
                    sendSupportReward(roomData.getZone(), roomData.getRound(), userId, rankNum, false);
                }

                // 给参赛的人发个奖励
                CrossJiuZhouProto.CrossJiuZhouSupportMailRewardWithBattleMsg.Builder builder = CrossJiuZhouProto.CrossJiuZhouSupportMailRewardWithBattleMsg.newBuilder();
                builder.setUserId(userId);
                builder.setRound(roomData.getRound());
                builder.setSupportReward(config.getSupportSelfReward());
                builder.setZoneType(roomData.getZone());
                sendPacket(Protocol.S_JIU_ZHOU_SUPPORT_MAIL_REWARD_WITH_BATTLE_FROM_CROSS, builder, userId);
            }

        }
        // 批量上榜
        uploadRank(roundConfig.getType(), roomData.getZone(), settleRanks);

        // 房间状态同步
        roomStateSyncMsg(roomData);

        // 对战结束,移除战场，可释放内存
        roomData.getGameData().getBattleDataList().clear();
    }

    private void sendSupportReward(int zoneType, int round, long userId, int rankNum, boolean success) {
        if (!supportCntMap.containsKey(round)) {
            return;
        }
        CrossUserBaseInfo userBaseInfo = getCrossUserBaseInfo(userId);
        if (userBaseInfo == null || userBaseInfo.getUserBaseInfo() == null) {
            return;
        }
        Map<Long, Map<Long, Integer>> map = supportCntMap.get(round);
        for (Map.Entry<Long, Map<Long, Integer>> entry : map.entrySet()) {
            // 谁声援的
            Long supportUserId = entry.getKey();
            // 声援了谁
            Map<Long, Integer> supportMap = entry.getValue();
            // 有声援才发放
            Integer supportCnt = supportMap.getOrDefault(userId, 0);
            if (supportCnt > 0) {
                CrossJiuZhouProto.CrossJiuZhouSupportMailRewardMsg.Builder builder = CrossJiuZhouProto.CrossJiuZhouSupportMailRewardMsg.newBuilder();
                builder.setSupportUserId(supportUserId);
                builder.setRound(round);
                builder.setTargetNickName(userBaseInfo.getUserBaseInfo().getNickName());
                builder.setSuccess(success);
                builder.setRankNum(rankNum);
                if (success) {
                    // 成功才有奖励
                    builder.setSupportReward(config.getSupportReward());
                }
                builder.setZoneType(zoneType);
                builder.setSupportCnt(supportCnt);
                sendPacket(Protocol.S_JIU_ZHOU_SUPPORT_MAIL_REWARD_FROM_CROSS, builder, supportUserId);
            }
        }
    }

    /**
     * 获取总积分
     */
    public long getTotalScore(CrossJiuZhouUserData userData, int roundType) {
        if (userData == null) {
            return 0;
        }
        long totalScore = 0;
        for (CrossJiuZhouUserRoundRecordData recordData : userData.getRecordDataMap().values()) {
            JiuZhouRoundConfig roundConfig = getJiuZhouRoundConfigById(recordData.getRound());
            if (roundConfig == null || roundConfig.getType() != roundType) {
                continue;
            }
            totalScore += recordData.getScore();
        }
        return totalScore;
    }

    /**
     * 获取总伤害
     */
    public long getTotalAttackDamage(CrossJiuZhouUserData userData, int roundType) {
        if (userData == null) {
            return 0;
        }
        long totalAttackDamage = 0;
        for (CrossJiuZhouUserRoundRecordData recordData : userData.getRecordDataMap().values()) {
            JiuZhouRoundConfig roundConfig = getJiuZhouRoundConfigById(recordData.getRound());
            if (roundConfig == null || roundConfig.getType() != roundType) {
                continue;
            }
            totalAttackDamage += recordData.getAttackDamage();
        }
        return totalAttackDamage;
    }


    /**
     * 创建用户战场数据
     *
     * @param roomId         房间id
     * @param userType       0:正常  1:镜像               99:npc（测试用）
     */
    public CrossJiuZhouGameBattleUserData createCrossJiuZhouGameBattleUserData(int roomId, CrossJiuZhouGameUserData myGameUserData, CrossJiuZhouGameUserData targetGameUserData, int userType) {
        CrossJiuZhouRoomData roomData = getRoomData(roomId);
        if (roomData == null || roomData.getGameData() == null) {
            return null;
        }
        CrossJiuZhouUserData userData = getUserData(myGameUserData.getUserId());
        if (userData == null) {
            return null;
        }
        // 游戏数据
        CrossJiuZhouGameData gameData = roomData.getGameData();
        CrossJiuZhouGameBattleUserData userBattleData = new CrossJiuZhouGameBattleUserData();

        userBattleData.setUserId(myGameUserData.getUserId());
        userBattleData.setUserType(userType);

        // 是否镜像
        boolean isMirror = userType == eJiuZhouBattleUserType.Mirror.getValue();
        // 血量
        if (isMirror) {
//            userBattleData.setHp(config.getMirrorHp());
            // 拉一下玩家的血量(镜像血量改成分配时的剩余血量)
            userBattleData.setHp(myGameUserData.getCurHp());
            // 设置一个镜像id
            userBattleData.setMirrorUserId(mirrorUserId.incrementAndGet());
        } else {
            // 拉一下玩家的血量
            userBattleData.setHp(myGameUserData.getCurHp());
        }
        userBattleData.setMaxHp(myGameUserData.getMaxHp());

        // 获取用户的对阵信息
        CrossJiuZhouDeployData deployData = getDeployData(roomId, myGameUserData.getUserId(), targetGameUserData.getUserId());
        if (deployData != null) {
            // 天时
            for (Integer skillId : deployData.getSkillId1List()) {
                if (skillId != 0) {
                    userBattleData.getSkillIds().add(skillId);
                }
            }
            // 棋书(镜像不生效)
            if (!isMirror && deployData.getSkillId2() != 0) {
                userBattleData.getSkillIds().add(deployData.getSkillId2());
            }

            // 拷贝门客数据用于战斗
            Map<Integer, CrossJiuZhouGamePatronsData> patronsDataMap = new ConcurrentHashMap<>();
            for (Integer patronsId : deployData.getPatronsIdList()) {
                CrossJiuZhouPatronsData patronsData = userData.getPatronsDataMap().get(patronsId);
                if (patronsData == null) {
                    continue;
                }
                CrossJiuZhouGamePatronsData gamePatronsData = new CrossJiuZhouGamePatronsData();
                gamePatronsData.setPatronsId(patronsData.getPatronsId());
                // 这里只设置基础战力就行
                gamePatronsData.setBaseAbility(new BigInteger(patronsData.getAbility().toString()));
                // 门客战斗数据
                patronsDataMap.put(patronsData.getPatronsId(), gamePatronsData);

            }
            userBattleData.setPatronsDataMap(patronsDataMap);

            // 创建时锁定一下最强门客
            userBattleData.putAttribute(MaxAbilityPatronsId, getBattleMaxAbilityPatronsId(userBattleData));
            // 创建时锁定一下最弱门客
            userBattleData.putAttribute(MinAbilityPatronsId, getBattleMinAbilityPatronsId(userBattleData));

            // 鼓舞的银两数(拿出来吧，后面省的遍历去布阵数据找)(镜像不生效)
            if (!isMirror) {
                userBattleData.setSilverBuff(new BigInteger(deployData.getSilverBuff().toString()));
            }
        }

        if (userType == eJiuZhouBattleUserType.Normal.getValue()) {
            // 更新一下战场数
            myGameUserData.setLastGameRound(gameData.getGameRound());
            myGameUserData.setLastBattleRound(0);
        }
        return userBattleData;
    }

    /**
     * 获取当前实时最高赚钱的门客id(可出战的门客 排除掉被禁用的)
     */
    private int getBattleMaxAbilityPatronsId(CrossJiuZhouGameBattleUserData battleUserData) {
        CrossJiuZhouGamePatronsData top = getBattleTopAbilityPatrons(battleUserData, 1);
        if (top == null) {
            return 0;
        }
        return top.getPatronsId();
    }

    /**
     * 获取当前实时最低赚钱的门客id(可出战的门客 排除掉被禁用的)
     */
    private int getBattleMinAbilityPatronsId(CrossJiuZhouGameBattleUserData battleUserData) {
        CrossJiuZhouGamePatronsData top = getBattleTopAbilityPatrons(battleUserData, 5);
        if (top == null) {
            return 0;
        }
        return top.getPatronsId();
    }

    /**
     * 获取前几的门客
     * @param battleUserData
     * @param topNum
     * @return
     */
    private CrossJiuZhouGamePatronsData getBattleTopAbilityPatrons(CrossJiuZhouGameBattleUserData battleUserData, int topNum) {
        // 禁用的门客ids
        List<Integer> banPatronsIds = battleUserData.getAttribute(BanPatronsIds, new ArrayList<>());
        List<CrossJiuZhouGamePatronsData> list = new ArrayList<>();
        for (CrossJiuZhouGamePatronsData patronsData : battleUserData.getPatronsDataMap().values()) {
            if (banPatronsIds.contains(patronsData.getPatronsId())) {
                // 禁用门客
                continue;
            }
            list.add(patronsData);
        }
        // 排个序  降序
        list.sort((o1, o2) -> o2.getAbility().compareTo(o1.getAbility()));
        if (list.size() < topNum) {
            return null;
        }
        return list.get(topNum - 1);
    }




    /**
     * 测试随机报名一些玩家
     */
    public void signRandomPlayerTest2(int npcNum) {
        logger.debug( "随机报名玩家开始");
        Map<Long, CrossUserBaseInfo> crossUserBaseInfoMap = new HashMap<>();

        // 当前的门客列表      区域，userid,门客
        Map<Integer, Map<Long, List<CrossJiuZhouPatronsData>>> patronsMap = new HashMap<>();
        List<CrossJiuZhouUserData> allUserList = new ArrayList<>();
        for (CrossJiuZhouUserData userData : userDataMap.values()) {
            if (userData == null) {
                continue;
            }
            if (CrossNpcUserMgr.isNpc(userData.getUserId())) {
                continue;
            }
            if (userData.getPatronsDataMap().isEmpty() || userData.getPatronsDataMap().size() < config.getSighLimit()) {
                continue;
            }
            if (userData.getZoneType() == 0) {
                continue;
            }
            allUserList.add(userData);
        }

        for (int i = 0; i < npcNum; i++) {
            // 拷贝这个人
            CrossJiuZhouUserData copyUserData = allUserList.get(i % allUserList.size());
            CrossUserBaseInfo crossNpcUserBaseInfo = CrossNpcUserMgr.createCrossNpcUserBaseInfo(copyUserData.getUserId());
            crossUserBaseInfoMap.put(crossNpcUserBaseInfo.getUserId(), crossNpcUserBaseInfo);

            Map<Long, List<CrossJiuZhouPatronsData>> map = patronsMap.getOrDefault(copyUserData.getZoneType(), new HashMap<>());
            List<CrossJiuZhouPatronsData> list = new ArrayList<>();
            for (CrossJiuZhouPatronsData patronsData : copyUserData.getPatronsDataMap().values()) {
                list.add(patronsData.clone());
            }
            map.put(crossNpcUserBaseInfo.getUserId(), list);
            patronsMap.put(copyUserData.getZoneType(), map);
        }

        BigInteger earn = BigInteger.ZERO;
        BigInteger ability = BigInteger.ZERO;
        // 随机报名
        Map<Long, List<CrossJiuZhouPatronsData>> zoneMap1 = patronsMap.getOrDefault(eJiuZhouGameZoneType.elite.getValue(), new HashMap<>());
        for (Long userId : zoneMap1.keySet()) {
            earn = earn.add(BigInteger.valueOf(100));
            ability = ability.add(BigInteger.valueOf(100));
            CrossUserBaseInfo crossUserBaseInfo = crossUserBaseInfoMap.get(userId);
            int i1 = signUp(userId,5, earn, zoneMap1.get(userId));
            if (i1 == 0) {
                logger.debug("随机报名玩家第一档：{},赚速{},能力{}", crossUserBaseInfo.getUserId(), earn, ability);
            }
        }

        BigInteger lockEarnSpeed = roundDataMap.get(2).getLockAbility();
        BigInteger secondEarn = new BigInteger(lockEarnSpeed.toString());
        BigInteger secondAbility = new BigInteger(lockEarnSpeed.toString());

        Map<Long, List<CrossJiuZhouPatronsData>> zoneMap2 = patronsMap.getOrDefault(eJiuZhouGameZoneType.master.getValue(), new HashMap<>());
        for (Long userId : zoneMap2.keySet()) {
            secondEarn = secondEarn.add(BigInteger.valueOf(10000));
            secondAbility = secondAbility.add(BigInteger.valueOf(10000));
            CrossUserBaseInfo crossUserBaseInfo = crossUserBaseInfoMap.get(userId);
            int i1 = signUp(userId,4, secondEarn, zoneMap2.get(userId));
            if (i1 == 0){
                logger.debug( "随机报名玩家第二档：{},赚速{},能力{}",crossUserBaseInfo.getUserId(),secondEarn,secondAbility);
            }
        }
        lockEarnSpeed = roundDataMap.get(3).getLockAbility();
        BigInteger thirdEarn = new BigInteger(lockEarnSpeed.toString());
        BigInteger thirdAbility = new BigInteger(lockEarnSpeed.toString());

        Map<Long, List<CrossJiuZhouPatronsData>> zoneMap3 = patronsMap.getOrDefault(eJiuZhouGameZoneType.king.getValue(), new HashMap<>());
        for (Long userId : zoneMap3.keySet()) {
            thirdEarn = thirdEarn.add(BigInteger.valueOf(10000));
            thirdAbility = thirdAbility.add(BigInteger.valueOf(10000));
            CrossUserBaseInfo crossUserBaseInfo = crossUserBaseInfoMap.get(userId);
            int i1 = signUp(userId,1, thirdEarn, zoneMap3.get(userId));
            if (i1 == 0){
                logger.debug( "随机报名第三档玩家：{},赚速{},能力{}",crossUserBaseInfo.getUserId(),thirdEarn,thirdAbility);
            }
        }
        logger.debug( "随机报名玩家结束,当前总共报名{}个玩家",userDataMap.size());
    }

    /**
     * 测试随机报名一些玩家
     */
    public void signRandomPlayerTest3(String param) {
        List<Integer> paramList = StringUtils.stringToIntegerList(param, "#");
        logger.debug("随机报名玩家开始");
        Map<Integer, CrossJiuZhouPatronsData> patronsDataMap = new HashMap<>();
        for (CrossJiuZhouUserData userData : userDataMap.values()) {
            if (userData == null) {
                continue;
            }
            if (CrossNpcUserMgr.isNpc(userData.getUserId())) {
                continue;
            }
            if (userData.getPatronsDataMap().isEmpty() || userData.getPatronsDataMap().size() < 80) {
                continue;
            }
            if (userData.getZoneType() == 0) {
                continue;
            }
            patronsDataMap = userData.getPatronsDataMap();
        }

        // 第一档 8000人
        // 随机报名
        for (int i = 0; i < paramList.get(0); i++) {
            // 区间内随机个战力
            double v = randomHelper.nextDouble(roundDataMap.get(2).getLockAbility().divide(BigInteger.valueOf(10)).doubleValue(), roundDataMap.get(2).getLockAbility().doubleValue());
            BigInteger ability = BigInteger.valueOf((long) v);
            List<BigInteger> list1 = randomHelper.randomSplitAbility(ability, BigInteger.valueOf(100000), 4, 60);

            CrossUserBaseInfo crossUserBaseInfo = CrossNpcUserMgr.createCrossNpcUserBaseInfo();
            crossUserBaseInfo.getUserBaseInfo().setNickName("npc" + BigDecimal.valueOf(ability.doubleValue()).divide(BigDecimal.valueOf(100000000), 2, RoundingMode.UP).doubleValue() + "亿");

            List<CrossJiuZhouPatronsData> pl = new ArrayList<>();
            int cnt = 0;
            for (CrossJiuZhouPatronsData patronsData : patronsDataMap.values()) {
                if (cnt >= list1.size()) {
                    continue;
                }
                BigInteger bigInteger = list1.get(cnt);
                if (bigInteger.compareTo(BigInteger.valueOf(100000)) < 0) {
                    continue;
                }
                CrossJiuZhouPatronsData clone = patronsData.clone();
                clone.setAbility(bigInteger);
                cnt++;
                pl.add(clone);
            }
            // 门客不够不要
            if (pl.size() < config.getSighLimit()) {
                continue;
            }
            int i1 = signUp(crossUserBaseInfo.getUserId(), 5, ability, pl);
            if (i1 == 0) {
                logger.debug("随机报名玩家第一档：{},能力{}", crossUserBaseInfo.getUserId(), ability);
            }
        }

        // 第二档 5000人
        // 随机报名
        for (int i = 0; i < paramList.get(1); i++) {
            // 区间内随机个战力
            double v = randomHelper.nextDouble(roundDataMap.get(2).getLockAbility().doubleValue(), roundDataMap.get(3).getLockAbility().doubleValue());
            BigInteger ability = BigInteger.valueOf((long) v);
            List<BigInteger> list1 = randomHelper.randomSplitAbility(ability, BigInteger.valueOf(10000), 4, 70);

            CrossUserBaseInfo crossUserBaseInfo = CrossNpcUserMgr.createCrossNpcUserBaseInfo();
            crossUserBaseInfo.getUserBaseInfo().setNickName("npc" + BigDecimal.valueOf(ability.doubleValue()).divide(BigDecimal.valueOf(100000000), 2, RoundingMode.UP).doubleValue() + "亿");

            List<CrossJiuZhouPatronsData> pl = new ArrayList<>();
            int cnt = 0;
            for (CrossJiuZhouPatronsData patronsData : patronsDataMap.values()) {
                if (cnt >= list1.size()) {
                    continue;
                }
                BigInteger bigInteger = list1.get(cnt);
                if (bigInteger.compareTo(BigInteger.valueOf(1000000)) < 0) {
                    continue;
                }
                CrossJiuZhouPatronsData clone = patronsData.clone();
                clone.setAbility(bigInteger);
                cnt++;
                pl.add(clone);
            }
            // 门客不够不要
            if (pl.size() < config.getSighLimit()) {
                continue;
            }
            int i1 = signUp(crossUserBaseInfo.getUserId(), 4, ability, pl);
            if (i1 == 0) {
                logger.debug("随机报名玩家第二档：{},能力{}", crossUserBaseInfo.getUserId(), ability);
            }
        }

        // 第三档 1000人
        // 随机报名
        for (int i = 0; i < paramList.get(2); i++) {
            // 区间内随机个战力
            double v = randomHelper.nextDouble(roundDataMap.get(3).getLockAbility().doubleValue(), roundDataMap.get(3).getLockAbility().multiply(BigInteger.valueOf(2)).doubleValue());
            BigInteger ability = BigInteger.valueOf((long) v);
            List<BigInteger> list1 = randomHelper.randomSplitAbility(ability, BigInteger.valueOf(10000), 4, 80);

            CrossUserBaseInfo crossUserBaseInfo = CrossNpcUserMgr.createCrossNpcUserBaseInfo();
            crossUserBaseInfo.getUserBaseInfo().setNickName("npc" + BigDecimal.valueOf(ability.doubleValue()).divide(BigDecimal.valueOf(100000000), 2, RoundingMode.UP).doubleValue() + "亿");

            List<CrossJiuZhouPatronsData> pl = new ArrayList<>();
            int cnt = 0;
            for (CrossJiuZhouPatronsData patronsData : patronsDataMap.values()) {
                if (cnt >= list1.size()) {
                    continue;
                }
                BigInteger bigInteger = list1.get(cnt);
                if (bigInteger.compareTo(BigInteger.valueOf(10000000)) < 0) {
                    continue;
                }
                CrossJiuZhouPatronsData clone = patronsData.clone();
                clone.setAbility(bigInteger);
                cnt++;
                pl.add(clone);
            }
            // 门客不够不要
            if (pl.size() < config.getSighLimit()) {
                continue;
            }
            int i1 = signUp(crossUserBaseInfo.getUserId(), 1, ability, pl);
            if (i1 == 0) {
                logger.debug("随机报名玩家第三档：{},能力{}", crossUserBaseInfo.getUserId(), ability);
            }
        }

        logger.debug("随机报名玩家结束,当前总共报名{}个玩家", userDataMap.size());
    }

    /**
     * 测试随机报名一些玩家
     */
    public void signRandomPlayerTest(int npcNum) {
        logger.debug( "随机报名玩家开始");
        List<CrossUserBaseInfo> crossUserBaseInfoList = new ArrayList<>();

        for (int i = 0; i < npcNum; i++) {
            crossUserBaseInfoList.add(CrossNpcUserMgr.createCrossNpcUserBaseInfo());
        }


        int size = crossUserBaseInfoList.size();
        int perZonePeople = size/3;

        BigInteger earn = BigInteger.ZERO;
        BigInteger ability = BigInteger.ZERO;
        // 随机报名
        for (int i = 0; i < perZonePeople; i++) {
            earn = earn.add(BigInteger.valueOf(100));
            ability = ability.add(BigInteger.valueOf(100));
            CrossUserBaseInfo crossUserBaseInfo = crossUserBaseInfoList.get(i);

            int i1 = signUp(crossUserBaseInfo.getUserId(), 5,earn, new ArrayList<>(getRandomPatronsDataMap().values()));
            if (i1 == 0){
                logger.debug( "随机报名玩家第一档：{},赚速{},能力{}",crossUserBaseInfo.getUserId(),earn,ability);
            }
        }
        BigInteger lockEarnSpeed = roundDataMap.get(2).getLockAbility();
        BigInteger secondEarn = new BigInteger(lockEarnSpeed.toString());
        BigInteger secondAbility = new BigInteger(lockEarnSpeed.toString());

        for (int i = perZonePeople; i < 2*perZonePeople; i++) {

            secondEarn = secondEarn.add(BigInteger.valueOf(10000));
            secondAbility = secondAbility.add(BigInteger.valueOf(10000));
            CrossUserBaseInfo crossUserBaseInfo = crossUserBaseInfoList.get(i);
            int i1 = signUp(crossUserBaseInfo.getUserId(), 4,secondEarn, new ArrayList<>(getRandomPatronsDataMap().values()));
            if (i1 == 0){
                logger.debug( "随机报名玩家第二档：{},赚速{},能力{}",crossUserBaseInfo.getUserId(),secondEarn,secondAbility);
            }
        }
        lockEarnSpeed = roundDataMap.get(3).getLockAbility();
        BigInteger thirdEarn = new BigInteger(lockEarnSpeed.toString());
        BigInteger thirdAbility = new BigInteger(lockEarnSpeed.toString());
        for (int i = 2*perZonePeople; i < size; i++) {
            thirdEarn = thirdEarn.add(BigInteger.valueOf(10000));
            thirdAbility = thirdAbility.add(BigInteger.valueOf(10000));
            CrossUserBaseInfo crossUserBaseInfo = crossUserBaseInfoList.get(i);
            int i1 = signUp(crossUserBaseInfo.getUserId(),1, thirdEarn, new ArrayList<>(getRandomPatronsDataMap().values()));
            if (i1 == 0){
                logger.debug( "随机报名第三档玩家：{},赚速{},能力{}",crossUserBaseInfo.getUserId(),thirdEarn,thirdAbility);
            }
        }
        logger.debug( "随机报名玩家结束,当前总共报名{}个玩家",userDataMap.size());
    }


    /**
     * 自动上阵门客
     *
     * @param roomData 房间数据
     */
    public void autoChoosePatronsProcess(CrossJiuZhouRoomData roomData) {
        int roomId = roomData.getRoomId();
        synchronized (getRoomLock(roomId)) {
            for (Long userId : roomData.getRoomUserIdList()) {
                CrossJiuZhouUserData userData = getUserData(userId);
                if (userData == null) {
                    continue;
                }
                List<CrossJiuZhouDeployData> deployDataList = getDeployDataList(roomId, userId);

                // npc 随机银两随机棋书
                if (debug) {
                    for (CrossJiuZhouDeployData deployData : deployDataList) {
                        if (deployData.getSkillId2() == 0) {
                            JiuZhouSkillConfig skillConfig = randomSkill(eJiuZhouBigSkillType.Skill2.getValue());
                            if (skillConfig != null) {
                                deployData.setSkillId2(skillConfig.getId());
                            }
                        }
                        if (deployData.getSilverBuff().compareTo(BigInteger.ZERO) == 0) {
                            deployData.setSilverBuff(BigInteger.valueOf(randomHelper.next(1, 999999999)));
                        }
                    }
                }
                // 已上阵的门客
                Set<Integer> deployPatronsIdList = new HashSet<>();
                for (CrossJiuZhouDeployData deployData : deployDataList) {
                    deployPatronsIdList.addAll(new ArrayList<>(deployData.getPatronsIdList()));
                }
                if (deployPatronsIdList.size() >= deployDataList.size() * 5) {
                    continue;
                }
                // 补齐门客
                List<CrossJiuZhouPatronsData> allPatrons = new ArrayList<>();
                for (CrossJiuZhouPatronsData patronsData : userData.getPatronsDataMap().values()) {
                    if (deployPatronsIdList.contains(patronsData.getPatronsId())) {
                        // 已上阵的忽略
                        continue;
                    }
                    allPatrons.add(patronsData);
                }
                // 排序一下,按赚钱降序
                allPatrons.sort((o1, o2) -> o2.getAbility().compareTo(o1.getAbility()));

                // 前35的门客拉出来
                List<CrossJiuZhouPatronsData> choosePatronsIdList = new ArrayList<>();
                // 还需要上阵多少个
                int needCnt = config.getSighLimit() - deployPatronsIdList.size();
                while (choosePatronsIdList.size() < needCnt && allPatrons.size() > 0) {
                    choosePatronsIdList.add(allPatrons.remove(0));
                }

                Collections.shuffle(choosePatronsIdList);

                // 开始塞人
                for (CrossJiuZhouDeployData deployData : deployDataList) {
                    while (deployData.getPatronsIdList().size() < 5 && choosePatronsIdList.size() > 0) {
                        deployData.getPatronsIdList().add(choosePatronsIdList.remove(0).getPatronsId());
                    }
                    deployData.setUpdateOption();
                }
            }
        }
        logger.info("房间id:{},自动补齐门客", roomId);
    }

    /**
     * 消息同步调用这个方法！！！
     *
     * @param roomId 房间id  id 传-1则获取活动内所有玩家
     */
    private ConcurrentHashSet<Long> getRoomUserListSync(int roomId) {
        ConcurrentHashSet<Long> list = new ConcurrentHashSet<>();
        CrossJiuZhouRoomData roomData = getRoomData(roomId);
        if (roomData != null) {
//            for (Long userId : roomData.getRoomUserIdList()) {
//                // 过滤npc
//                if (CrossNpcUserMgr.isNpc(userId)) {
//                    continue;
//                }
//                list.add(userId);
//            }
//            // 添加观战玩家
//            list.addAll(roomData.getWatchUserIdList());
            // 只同步观战用户
            for (Long userId : roomData.getWatchUserIdList()) {
                // 过滤npc
                if (CrossNpcUserMgr.isNpc(userId)) {
                    continue;
                }
                list.add(userId);
            }
        }
        return list;
    }

    /**
     * 创建布阵信息
     *
     * @param userId         用户id
     * @param opponentUserId 对手用户id
     * @param skillId1       天时
     */
    private void createDeployData(int roomId, long userId, long opponentUserId, int skillId1) {
        CrossJiuZhouRoomData roomData = getRoomData(roomId);
        if (roomData == null) {
            return;
        }
        CrossJiuZhouUserData userData = getUserData(userId);
        if (userData == null) {
            return;
        }
        synchronized (getRoomLock(roomId)) {
            CrossJiuZhouDeployData deployData = new CrossJiuZhouDeployData();
            deployData.setActivityId(config.getActivityInfo().getActivityId());
            deployData.setRound(roomData.getRound());
            deployData.setRoomId(roomId);
            deployData.setUserId(userId);
            deployData.setOpponentUserId(opponentUserId);
            deployData.setSkillId2(0);
            deployData.setSilverBuff(BigInteger.ZERO);
            // 天时
            deployData.setSkillId1List(Collections.singletonList(skillId1));
            deployData.setPatronsIdList(new ArrayList<>());
            deployData.setInsertOption();
            Map<Long, List<CrossJiuZhouDeployData>> roundDeployMap = deployDataMap.getOrDefault(roomId, new ConcurrentHashMap<>());
            List<CrossJiuZhouDeployData> list = roundDeployMap.getOrDefault(userId, new ArrayList<>());
            list.add(deployData);
            roundDeployMap.put(userId, list);
            deployDataMap.put(roomId, roundDeployMap);
        }
    }

    /**
     * 获取房间数据
     *
     * @param roomId 房间id
     */
    public CrossJiuZhouRoomData getRoomData(int roomId) {
        if (!roomDataMap.containsKey(roomId)) {
            return null;
        }
        return roomDataMap.get(roomId);
    }

    /**
     * 获取当前轮次布阵数据
     *
     * @param userId         玩家Id
     * @param opponentUserId 对手玩家Id
     */
    public CrossJiuZhouDeployData getDeployData(int roomId, long userId, long opponentUserId) {
        List<CrossJiuZhouDeployData> deployDataList = getDeployDataList(roomId, userId);
        for (CrossJiuZhouDeployData deployData : deployDataList) {
            if (deployData.getOpponentUserId() == opponentUserId) {
                return deployData;
            }
        }
        return null;
    }

    /**
     * 布阵列表
     */
    public List<CrossJiuZhouDeployData> getDeployDataList(int roomId, long userId) {
        List<CrossJiuZhouDeployData> list = new ArrayList<>();
        if (!deployDataMap.containsKey(roomId)) {
            return list;
        }
        if (!deployDataMap.get(roomId).containsKey(userId)) {
            return list;
        }
        List<CrossJiuZhouDeployData> deployDataList = deployDataMap.get(roomId).get(userId);
        for (CrossJiuZhouDeployData deployData : deployDataList) {
            if (deployData.getUserId() == userId) {
                list.add(deployData);
            }
        }
        return list;
    }

    /**
     * 获取用户数据
     */
    public CrossJiuZhouUserData getUserData(long userId) {
        return userDataMap.get(userId);
    }

    /**
     * 初始化玩家数据
     */
    public CrossJiuZhouUserData initUserData(long userId) {
        if (userDataMap.containsKey(userId)) {
            return userDataMap.get(userId);
        }
        synchronized (getPlayerLock(userId)) {
            if (userDataMap.containsKey(userId)) {
                return userDataMap.get(userId);
            }
            CrossJiuZhouUserData userData = new CrossJiuZhouUserData();
            userData.setActivityId(config.getActivityInfo().getActivityId());
            userData.setUserId(userId);
            // 不存在任何房间
            userData.setRoomIdMap(new HashMap<>());
            userData.setPatronsAbility(BigInteger.ZERO);
            userData.setZoneType(0);        //未分区
            userData.setLastRoundId(0);     //未晋级
            userData.setInsertOption();
            userDataMap.put(userId, userData);
            return userData;
        }


    }


    /**
     * 房间逻辑定时器
     */
    public void roomLogicTask() {
        for (CrossJiuZhouRoomData roomData : roomDataMap.values()) {
            if (roomData.getState() == eJiuZhouRoomStateType.End.getValue()) {
                // 房间结束了不再处理
                continue;
            }
            // 当前赛区
            int zone = roomData.getZone();
            CrossJiuZhouRoundData roundData = roundDataMap.get(zone);
            if (roundData == null) {
                continue;
            }
            // 轮次已经过了
            if (roundData.getCurRoundId() != roomData.getRound()) {
                continue;
            }
            // 数据准备中不处理
            if (roundData.getCurRoundState() == eJiuZhouRoundTime.serverMatching.getValue()) {
                continue;
            }
            // 备战进程
            deployProcess(roomData);
            // 创建战场进程(补齐门客,生成游戏)
            prepareBattleProcess(roomData);
            // 战斗进程(生成战场,锦囊推送,战斗逻辑,战斗结算)
            battleProcess(roomData);

//            if (roomData.getGameData() != null) {
//                logger.info("房间id:{},房间状态:{},战场场次:{},战斗进程状态:{}", roomData.getRoomId(), eJiuZhouRoomStateType.forValue(roomData.getState()).getName(), roomData.getGameData().getGameRound(), eJiuZhouGameStageType.forValue(roomData.getGameData().getStageType()).getName());
//            } else {
//                logger.info("房间id:{},房间状态:{},无战斗进程", roomData.getRoomId(), eJiuZhouRoomStateType.forValue(roomData.getState()).getName());
//            }
        }
    }

    /**
     * 准备战斗进程(生成战场数据)
     *
     * @param roomData 房间数据
     */
    public void prepareBattleProcess(CrossJiuZhouRoomData roomData) {
        if (roomData == null) {
            return;
        }
        // 只有在备战期的房间才可以切换状态
        if (roomData.getState() != eJiuZhouRoomStateType.Deploy.getValue()) {
            return;
        }
        CrossJiuZhouRoundData roundData = roundDataMap.get(roomData.getZone());
        if (roundData == null) {
            return;
        }
        JiuZhouRoundConfig roundConfig = getJiuZhouRoundConfigById(roundData.getCurRoundId());
        if (roundConfig == null) {
            return;
        }
        // 需要到开战准备期
        if (roundData.getCurRoundState() != eJiuZhouRoundTime.fightPrepare.getValue() && roundData.getCurRoundState() != eJiuZhouRoundTime.fight.getValue()) {
            return;
        }
        logger.info("房间id:{},进入等待战斗期", roomData.getRoomId());

        // 检测门客进程
        autoChoosePatronsProcess(roomData);

        // 创建游戏进程
        createGameDataProcess(roomData);

        // 创建用户信息进程
        createGameUserDataProcess(roomData);

        // 房间状态改一下
        roomData.setState(eJiuZhouRoomStateType.Battle.getValue());
        // 改一下状态
        roomData.getGameData().setStageType(eJiuZhouGameStageType.Prepare.getValue());
        roomData.getGameData().setNextStageTime(getTimeStampAfterBegin(roundConfig.getTimeList().get(2)));
    }


    /**
     * 生成游戏数据
     *
     * @param roomData 房间数据
     */
    public void createGameDataProcess(CrossJiuZhouRoomData roomData) {
        if (roomData == null) {
            return;
        }
        // 只有在备战期才能生成,防止覆盖了战斗数据
        if (roomData.getState() != eJiuZhouRoomStateType.Deploy.getValue()) {
            return;
        }
        // 生成战场
        CrossJiuZhouGameData gameData = new CrossJiuZhouGameData();
        // 准备期
        gameData.setGameRound(0);
        // 等待匹配状态
        gameData.setStageType(eJiuZhouGameStageType.WaitMatch.getValue());

        roomData.setGameData(gameData);
        logger.info("房间id:{},生成游戏数据", roomData.getRoomId());
    }

    /**
     * 创建用户信息
     *
     * @param roomData 房间信息
     */
    public void createGameUserDataProcess(CrossJiuZhouRoomData roomData) {
        if (roomData == null || roomData.getGameData() == null) {
            return;
        }
        // 只有在备战期才能生成,防止覆盖了战斗数据--
        if (roomData.getState() != eJiuZhouRoomStateType.Deploy.getValue()) {
            return;
        }
        long nowTime = System.currentTimeMillis();
        CrossJiuZhouRankListModel rankListModel = roomBattleRankMap.getOrDefault(roomData.getRoomId(), new CrossJiuZhouRankListModel(new JiuZhouRoomBattleRankSort()));
        Map<Long, CrossJiuZhouGameUserData> gameUserDataMap = new ConcurrentHashMap<>();
        List<JiuZhouRank> ranks = new ArrayList<>();
        for (Long userId : roomData.getRoomUserIdList()) {
            CrossJiuZhouUserData userData = getUserData(userId);
            if (userData == null) {
                continue;
            }
            // 门客不够
            if (userData.getPatronsDataMap().size() < config.getSighLimit()) {
                logger.error("用户:{}，门客不够,门客数量:{}", userData.getUserId(), userData.getPatronsDataMap().size());
                continue;
            }
            CrossJiuZhouGameUserData gameUserData = new CrossJiuZhouGameUserData();
            gameUserData.setUserId(userId);
            gameUserData.setLastGameRound(0);
            gameUserData.setLastBattleRound(0);
            // 初始最大血量
            gameUserData.setMaxHp(getJiuZhouRoundConfigById(roomData.getRound()).getInitHp());
            gameUserData.setCurHp(gameUserData.getMaxHp());
            gameUserDataMap.put(userId, gameUserData);
            // param 回合数#血量#伤害值#门客赚钱
            List<Long> paramList = new ArrayList<>();
            if (gameUserData.getLastGameRound() > 0 && gameUserData.getCurHp() > 0) {
                paramList.add((long) config.getMaxFightRound());
            } else {
                paramList.add((long) gameUserData.getLastBattleRound());
            }
            paramList.add((long) gameUserData.getCurHp());
            paramList.add((long) gameUserData.getAttackDamage());
            paramList.add(userData.getPatronsAbility().longValue());
            ranks.add(new JiuZhouRank(config.getActivityId(), userData.getUserId(), gameUserData.getLastGameRound(), paramList, nowTime));
        }
        roomData.getGameData().setGameUserDataMap(gameUserDataMap);
        logger.info("房间id:{},生成游戏用户数据", roomData.getRoomId());

        // 更新下榜单
        rankListModel.batchRankChange(ranks);
        roomBattleRankMap.put(roomData.getRoomId(), rankListModel);
    }

    /**
     * 房间里还有几个人活着
     */
    public List<Long> roomALiveUserList(CrossJiuZhouRoomData roomData) {
        List<Long> userIdList = new ArrayList<>();
        if (roomData == null || roomData.getGameData() == null) {
            return userIdList;
        }
        for (CrossJiuZhouGameUserData gameUserData : roomData.getGameData().getGameUserDataMap().values()) {
            if (gameUserData.getCurHp() > 0) {
                userIdList.add(gameUserData.getUserId());
            }
        }
        return userIdList;
    }

    /**
     * 生成战场进程
     *
     * @param roomData 房间数据
     */
    private void battleMatchProcess(CrossJiuZhouRoomData roomData) {
        if (roomData == null) {
            return;
        }
        if (roomData.getState() == eJiuZhouRoomStateType.End.getValue()) {
            // 房间已结算
            return;
        }
        CrossJiuZhouGameData gameData = roomData.getGameData();
        if (gameData == null) {
            return;
        }
        // 等待匹配状态才能匹配
        if (roomData.getState() != eJiuZhouRoomStateType.Battle.getValue() || roomData.getGameData().getStageType() != eJiuZhouGameStageType.Prepare.getValue()) {
            return;
        }
        long nowTime = System.currentTimeMillis();
        if (nowTime < roomData.getGameData().getNextStageTime()) {
            // 时间未到
            return;
        }
        // 检测游戏
        checkGameOver(roomData);

        // 待匹配的玩家
        List<CrossJiuZhouGameUserData> needMatchUserList = new ArrayList<>();
        for (CrossJiuZhouGameUserData gameUserData : roomData.getGameData().getGameUserDataMap().values()) {
            if (gameUserData.getCurHp() <= 0) {
                continue;
            }
            needMatchUserList.add(gameUserData);
        }
        // 记录下战斗场次
        roomData.getGameData().setGameRound(roomData.getGameData().getGameRound() + 1);

        // 战场数据
        List<CrossJiuZhouGameBattleData> battleDataList = battleMatch(roomData, needMatchUserList);
        if (!battleDataList.isEmpty()) {
            roomData.getGameData().setBattleDataList(battleDataList);
        }

        // 房间状态同步(！！匹配完再推状态)
        roomStateSyncMsg(roomData);
    }

    /**
     * 生成战场匹配
     *
     * @param roomData          房间数据
     * @param needMatchUserList 待匹配用户
     */
    public List<CrossJiuZhouGameBattleData> battleMatch(CrossJiuZhouRoomData roomData, List<CrossJiuZhouGameUserData> needMatchUserList) {
        // 战场数据
        List<CrossJiuZhouGameBattleData> battleDataList = new ArrayList<>();

        // 已匹配用户ID集合
        Set<Long> matchedUserIds = new HashSet<>();

        // 处理奇数情况，随机选择一个幸运玩家和镜像
        if (needMatchUserList.size() % 2 != 0) {
            CrossJiuZhouGameUserData luckUser = randomLuckyUser(needMatchUserList); // 确定幸运儿
            CrossJiuZhouGameUserData mirrorUser = randomMirrorUser(needMatchUserList, luckUser); // 选择镜像玩家

            // 如果找到了合适的镜像玩家
            if (luckUser != null && mirrorUser != null) {
                matchedUserIds.add(luckUser.getUserId());

                // 更新幸运儿的镜像对战次数
                luckUser.incrementMirrorBattleCnt(mirrorUser.getUserId());

                // 创建战场数据，将镜像标记为镜像玩家
                CrossJiuZhouGameBattleData mirrorBattleData = generateBattleData(roomData.getRoomId(), luckUser, mirrorUser, eJiuZhouBattleUserType.Mirror.getValue());
                battleDataList.add(mirrorBattleData);
            }
        }


        // 转换为用户ID到用户数据的映射
        Map<Long, CrossJiuZhouGameUserData> userDataMap = needMatchUserList.stream()
                .filter(u -> u.getCurHp() > 0) // 过滤掉血量为0的玩家
                .collect(Collectors.toMap(CrossJiuZhouGameUserData::getUserId, Function.identity()));

        // 获取房间内排名
        List<JiuZhouRank> rankList = getRoomRankList(roomData.getRoomId());
        List<Long> rankedUserIds = rankList.stream().map(JiuZhouRank::getUserId).filter(userDataMap::containsKey).collect(Collectors.toList());

        // 从排名第1的玩家开始，为每个玩家分配对战对手
        for (Long rankedUserId : rankedUserIds) {
            if (matchedUserIds.contains(rankedUserId)) {
                continue; // 跳过已匹配的玩家
            }

            CrossJiuZhouGameUserData currentUserData = userDataMap.get(rankedUserId);
            Long selectedOpponentId = findLeastBattledOpponent(currentUserData, userDataMap, matchedUserIds);

            // 如果找到了对手，创建战场数据并更新对战次数
            if (selectedOpponentId != null) {
                CrossJiuZhouGameUserData opponentUserData = userDataMap.get(selectedOpponentId);
                matchedUserIds.add(rankedUserId);
                matchedUserIds.add(selectedOpponentId);

                CrossJiuZhouGameBattleData battleData = generateBattleData(roomData.getRoomId(), currentUserData, opponentUserData, eJiuZhouBattleUserType.Normal.getValue());
                battleDataList.add(battleData);

                // 更新对战次数
                currentUserData.incrementBattleCnt(selectedOpponentId);
                opponentUserData.incrementBattleCnt(rankedUserId);
            }
        }
        return battleDataList;
    }

    private Long findLeastBattledOpponent(CrossJiuZhouGameUserData currentUserData, Map<Long, CrossJiuZhouGameUserData> userDataMap, Set<Long> matchedUserIds) {
        int minBattleCount = Integer.MAX_VALUE;

        List<Long> leastBattledOpponents = new ArrayList<>();

        for (Long potentialOpponentId : userDataMap.keySet()) {
            // 跳过已匹配的玩家或自己
            if (matchedUserIds.contains(potentialOpponentId) || potentialOpponentId.equals(currentUserData.getUserId())) {
                continue;
            }

            // 获取与当前用户的对战次数
            int battleCountWithCurrentUser = currentUserData.getBattleCnt().getOrDefault(potentialOpponentId, 0);
            if (battleCountWithCurrentUser < minBattleCount) {
                minBattleCount = battleCountWithCurrentUser;
                leastBattledOpponents.clear();
                leastBattledOpponents.add(potentialOpponentId);
            } else if (battleCountWithCurrentUser == minBattleCount) {
                leastBattledOpponents.add(potentialOpponentId);
            }
        }
        if (leastBattledOpponents.isEmpty()) {
            return null;
        }
        return leastBattledOpponents.get(randomHelper.next(0, leastBattledOpponents.size()));
    }

    /**
     * 创建战场数据
     * type 0 敌人是正常的  1敌人是镜像
     */
    private CrossJiuZhouGameBattleData generateBattleData(int roomId, CrossJiuZhouGameUserData myGameUserData, CrossJiuZhouGameUserData targetGameUserData, int targetUsertype) {
        CrossJiuZhouGameBattleData battleData = new CrossJiuZhouGameBattleData();
        battleData.setRoomId(roomId);
        battleData.setBattleRound(0);

        CrossJiuZhouGameBattleUserData userBattleData1 = createCrossJiuZhouGameBattleUserData(roomId, myGameUserData, targetGameUserData, eJiuZhouBattleUserType.Normal.getValue());
        battleData.setBattleUser1(userBattleData1);

        CrossJiuZhouGameBattleUserData userBattleData2 = createCrossJiuZhouGameBattleUserData(roomId, targetGameUserData, myGameUserData, targetUsertype);
        battleData.setBattleUser2(userBattleData2);

        return battleData;
    }

    /**
     * 随机一个镜像
     */
    private CrossJiuZhouGameUserData randomMirrorUser(List<CrossJiuZhouGameUserData> needMatchUserList, CrossJiuZhouGameUserData luckUser) {
        if (luckUser == null) {
            return null;
        }
        // 选一个对战次数最少的
        int minCnt = Integer.MAX_VALUE;
        List<CrossJiuZhouGameUserData> leastBattleUsers = new ArrayList<>();

        for (CrossJiuZhouGameUserData gameUserData : needMatchUserList) {
            if (gameUserData.getCurHp() <= 0) {
                continue;
            }
            if (gameUserData.getUserId() == luckUser.getUserId()) {
                continue;
            }
            Integer mirrorCnt = luckUser.getMirrorBattleCnt().getOrDefault(gameUserData.getUserId(), 0);
            if (mirrorCnt < minCnt) {
                minCnt = mirrorCnt;
                leastBattleUsers.clear();
                leastBattleUsers.add(gameUserData);
            } else if (mirrorCnt == minCnt) {
                leastBattleUsers.add(gameUserData);
            }
        }
        if (leastBattleUsers.isEmpty()) {
            return null;
        }
        return leastBattleUsers.get(randomHelper.next(0, leastBattleUsers.size()));
    }

    /**
     * 随机一个幸运儿
     */
    private CrossJiuZhouGameUserData randomLuckyUser(List<CrossJiuZhouGameUserData> userDataList) {
        List<CrossJiuZhouGameUserData> luckyCandidates = new ArrayList<>();
        int minCnt = Integer.MAX_VALUE;
        for (CrossJiuZhouGameUserData gameUserData : userDataList) {
            // 死了不选
            if (gameUserData.getCurHp() <= 0) {
                continue;
            }
            int totalMirrorBattleCnt = gameUserData.getTotalMirrorBattleCnt();
            if (totalMirrorBattleCnt < minCnt) {
                minCnt = totalMirrorBattleCnt;
                luckyCandidates.clear();
                luckyCandidates.add(gameUserData);
            } else if (totalMirrorBattleCnt == minCnt) {
                luckyCandidates.add(gameUserData);
            }
        }
        if (luckyCandidates.isEmpty()) {
            return null;
        }
        return luckyCandidates.get(randomHelper.next(0, luckyCandidates.size()));
    }

    /**
     * 跨服登录，数据同步
     *
     * @param userId 用户id
     */
    public void crossLoginByCmd(long userId) {
        CrossJiuZhouUserData userData = getUserData(userId);
        if (userData == null) {
            return;
        }
        int round = getCurRoundByUserId(userId);
        // 登录数据同步
        sendPacket(Protocol.U_JIU_ZHOU_LOGIN_SYNC, parseJiuZhouCrossUserDataMsg(userId, round), userId);

        // 同步下数据到区服
        crossUserDataSync(userId);
    }

    /**
     * 同步跨服数据到区服
     */
    public void crossUserDataSync(long userId) {
        CrossJiuZhouUserData userData = getUserData(userId);
        if (userData == null) {
            return;
        }
        CrossJiuZhouProto.CrossJiuZhouCrossUserDataSyncMsg.Builder builder = CrossJiuZhouProto.CrossJiuZhouCrossUserDataSyncMsg.newBuilder();
        builder.setActivityId(config.getActivityId());
        builder.setUserId(userData.getUserId());
        builder.setZoneType(userData.getZoneType());
        builder.setLastRoundId(userData.getLastRoundId());

        sendPacket(Protocol.S_JIU_ZHOU_CROSS_USER_DATA_FROM_CROSS, builder, userId);
    }

    /**
     * 阶段变换逻辑
     */
    public void stageChangeTask() {
        synchronized (roundDataMap) {
            //遍历每一个赛区去查看
            for (CrossJiuZhouRoundData roundData : roundDataMap.values()) {
                //还没锁完名单 刚刚加载活动
                //这时候轮次是0,轮次类型是未开启,轮次状态是服务器处理中
                if (roundData.getIsLockAbility() == 0) {
                    //检测一下是否可以锁定名单
                    checkServerUploadData(roundData);
                }
                //如果还没锁完 就不检测底下是否可以开启下一轮
                if (roundData.getIsLockAbility() == 0) {
                    continue;
                }
                //活动已经锁完了名单
                //当前轮次是否是0进行判断,如果是0 特殊处理
                if (roundData.getCurRoundId() == 0) {
                    //判断是否结束报名进行第一轮了
                    int endSignUpTime = config.getJiuZhouTime().get(1);
                    //获取结束报名时间戳
                    long endSignTimeStamp = getTimeStampAfterBegin(endSignUpTime);
                    //如果当前时间小于结束报名时间,就暂时不进行进入第一轮的检测
                    if (System.currentTimeMillis() < endSignTimeStamp) {
                        continue;
                    }
                    //如果当前时间大于结束报名时间,就进行进入第一轮的检测
                    JiuZhouRoundConfig firstRound = getJiuZhouRoundConfigById(1);
                    if (firstRound == null) {
                        logger.error("九州活动第一轮配置不存在,activityId:{}", config.getActivityInfo().getActivityId());
                        continue;
                    }
                    //开始第一轮的匹配时间
                    int startMatchingTime = firstRound.getTimeList().get(0);
                    long timeStampAfterBegin = getTimeStampAfterBegin(startMatchingTime);
                    if (System.currentTimeMillis() < timeStampAfterBegin) {
                        continue;
                    }
                    //如果当前时间大于开始匹配时间,就进行进入第一轮的匹配
                    //如果服务器数据状态是未锁定名单,就进行锁定名单
                    if (roundData.getServerDataState() == eJiuZhouServerDataState.notStart.getValue()){
                        boolean lockSuccess = lockRoundUser(roundData.getZoneType(), null, firstRound);
                        if (!lockSuccess) {
                            logger.error("九州活动第一轮锁定名单失败,activityId:{}", config.getActivityInfo().getActivityId());
                            continue;
                        }
                        roundData.setServerDataState(eJiuZhouServerDataState.noMarch.getValue());
                    }
                    //如果服务器数据状态是未匹配,就进行匹配
                    if (roundData.getServerDataState() == eJiuZhouServerDataState.noMarch.getValue()){
                        boolean startMatching = startMatching(roundData.getZoneType(), firstRound);
                        if (!startMatching) {
                            logger.error("九州活动第一轮开始匹配失败,activityId:{}", config.getActivityInfo().getActivityId());
                            continue;
                        }
                    }
                    roundData.setCurRoundId(1);
                    roundData.setRoundType(firstRound.getType());
                    //匹配完了 进备战期吧
                    roundData.setCurRoundState(eJiuZhouRoundTime.prepare.getValue());
                    //重置一下下一阶段的数据准备
                    roundData.setServerDataState(eJiuZhouServerDataState.notStart.getValue());


                } else {
                    //如果是后面的轮次
                    JiuZhouRoundConfig thisRoundConfig = getJiuZhouRoundConfigById(roundData.getCurRoundId());
                    if (thisRoundConfig == null) {
                        logger.error("九州活动轮次配置不存在,activityId:{},roundId:{}", config.getActivityId(), roundData.getCurRoundId());
                        continue;
                    }
                    List<Integer> timeList = thisRoundConfig.getTimeList();
                    long matchingTime = getTimeStampAfterBegin(timeList.get(0));
                    long prepareStartTime = getTimeStampAfterBegin(timeList.get(1));
                    long prepareEndTime = getTimeStampAfterBegin(timeList.get(2));
                    long battleEndTime = getTimeStampAfterBegin(timeList.get(3));
                    //先检测是不是还没有匹配
                    JiuZhouRoundConfig nextRoundConfig = getJiuZhouRoundConfigById(roundData.getCurRoundId() + 1);


                    if (roundData.getCurRoundState() == eJiuZhouRoundTime.serverMatching.getValue()) {
                        //如果当前时间大于匹配时间,就进行进入备战期1的检测
                        if (System.currentTimeMillis() < matchingTime) {
                            continue;
                        }
                        //如果当前时间大于备战期1的开始时间,并且数据属于未初始化
                        //如果服务器数据状态是未锁定名单,就进行锁定名单
                        if (roundData.getServerDataState() == eJiuZhouServerDataState.notStart.getValue()){
                            JiuZhouRoundConfig beforeRoundConfig = getJiuZhouRoundConfigById(roundData.getCurRoundId() - 1);
                            boolean lockSuccess = lockRoundUser(roundData.getZoneType(), beforeRoundConfig, thisRoundConfig);
                            if (!lockSuccess) {
                                logger.error("九州活动第{}轮锁定名单失败,activityId:{}", roundData.getCurRoundId(), config.getActivityInfo().getActivityId());
                                continue;
                            }
                            roundData.setServerDataState(eJiuZhouServerDataState.noMarch.getValue());
                        }
                        //如果服务器数据状态是未匹配,就进行匹配
                        if (roundData.getServerDataState() == eJiuZhouServerDataState.noMarch.getValue()){
                            boolean startMatching = startMatching(roundData.getZoneType(), thisRoundConfig);
                            if (!startMatching) {
                                logger.error("九州活动第{}轮开始匹配失败,activityId:{}", roundData.getCurRoundId(), config.getActivityInfo().getActivityId());
                                continue;
                            }
                            roundData.setServerDataState(eJiuZhouServerDataState.noLockGuess.getValue());
                        }
                        //如果服务器数据状态是未锁定竞猜数据,就进行锁定竞猜数据
                        if (roundData.getServerDataState() == eJiuZhouServerDataState.noLockGuess.getValue()){
                            boolean lockCrossGuessData = lockCrossGuessData(thisRoundConfig, roundData.getZoneType());
                            if (!lockCrossGuessData) {
                                logger.error("九州活动第{}轮锁定竞猜数据失败,activityId:{}", roundData.getCurRoundId(), config.getActivityInfo().getActivityId());
                                continue;
                            }
                        }
                        logger.debug("九州活动第{}轮开始匹配成功,activityId:{}", roundData.getCurRoundId(), config.getActivityInfo().getActivityId());
                        //匹配完了 进备战期吧
                        roundData.setCurRoundState(eJiuZhouRoundTime.prepare.getValue());
                        //重置一下下一阶段的数据准备
                        roundData.setServerDataState(eJiuZhouServerDataState.notStart.getValue());
                    }
                    //如果当前时间大于备战期1的开始时间,就进行进入开战准备期的检测
                    if (roundData.getCurRoundState() == eJiuZhouRoundTime.prepare.getValue()) {
                        if (System.currentTimeMillis() < prepareStartTime) {
                            continue;
                        }
                        logger.debug("九州活动第{}轮进入备战期,activityId:{}", roundData.getCurRoundId(), config.getActivityInfo().getActivityId());
                        roundData.setCurRoundState(eJiuZhouRoundTime.fightPrepare.getValue());
                    }
                    //如果当前时间大于备战期的结束时间,就进行进入开战期
                    if (roundData.getCurRoundState() == eJiuZhouRoundTime.fightPrepare.getValue()) {
                        if (System.currentTimeMillis() < prepareEndTime) {
                            continue;
                        }
                        logger.debug("九州活动第{}轮进入开战期,activityId:{}", roundData.getCurRoundId(), config.getActivityInfo().getActivityId());
                        roundData.setCurRoundState(eJiuZhouRoundTime.fight.getValue());
                    }
                    //如果当前时间大于开战期的结束时间,就进行进入战斗结束期
                    if (roundData.getCurRoundState() == eJiuZhouRoundTime.fight.getValue()) {
                        if (System.currentTimeMillis() < battleEndTime) {
                            continue;
                        }
                        logger.debug("九州活动第{}轮进入展示期开始检测,activityId:{}",roundData.getCurRoundId(), config.getActivityInfo().getActivityId());
                        List<Integer> roundRoomIdList = roundRoomIdMap.get(roundData.getCurRoundId());
                        if (roundRoomIdList == null || roundRoomIdList.isEmpty()){
                            logger.error("九州活动第{}轮进入展示期没有找到房间数据,activityId:{}", roundData.getCurRoundId(), config.getActivityInfo().getActivityId());
                            continue;
                        }
                        boolean canEnterShow = true;
                        //检测房间数据
                        for (int roomId:roundRoomIdList){
                            CrossJiuZhouRoomData roomData = roomDataMap.get(roomId);
                            if (roomData == null){
                                canEnterShow = false;
                                logger.error("九州活动第{}轮进入展示期没有找到房间数据,activityId:{}", roundData.getCurRoundId(), config.getActivityInfo().getActivityId());
                                break;
                            }
                            //如果不是这个赛区的 找下一个房间
                            if (roomData.getZone() != roundData.getZoneType()){
                                continue;
                            }
                            //
                            if (roomData.getState() != eJiuZhouRoomStateType.End.getValue()){
                                canEnterShow = false;
                                logger.error("九州活动第{}轮进入展示期房间:{}未结束,activityId:{}", roundData.getCurRoundId(),roomData.getRoomId(), config.getActivityInfo().getActivityId());
                                break;
                            }
                        }
                        if (!canEnterShow){
                            continue;
                        }
                        // 处理下用户的本轮的记录
                        for (int roomId : roundRoomIdList) {
                            CrossJiuZhouRoomData roomData = getRoomData(roomId);
                            if (roomData == null) {
                                continue;
                            }
                            for (Long userId : roomData.getRoomUserIdList()) {
                                CrossJiuZhouUserData userData = getUserData(userId);
                                if (userData == null) {
                                    continue;
                                }
                                //拿出轮次map记录数据
                                CrossJiuZhouUserRoundRecordData recordData = userData.getRecordData(thisRoundConfig.getId());
                                if (recordData != null) {
                                    // 记录一下排名
                                    recordData.setRank(getRankByIdAndType(userId, roomData.getZone(), thisRoundConfig.getType()));
                                }
                            }
                        }
                        roundData.setCurRoundState(eJiuZhouRoundTime.show.getValue());
                        logger.info("九州活动第{}轮进入展示期,activityId:{}", roundData.getCurRoundId(), config.getActivityInfo().getActivityId());
                    }
                    //如果当前时间大于展示期的结束时间,就进行进入下一轮
                    if (roundData.getCurRoundState() == eJiuZhouRoundTime.show.getValue()){
                        JiuZhouRoundConfig nextRound = nextRoundConfig;
                        //如果最后一轮了 不需要检测
                        if (nextRound == null){
                            continue;
                        }
                        List<Integer> nextRoundTimeList = nextRound.getTimeList();
                        long nextRoundStartTime = getTimeStampAfterBegin(nextRoundTimeList.get(0));
                        if (System.currentTimeMillis() < nextRoundStartTime) {
                            continue;
                        }
                        //下一场
                        roundData.setCurRoundId(nextRound.getId());
                        roundData.setRoundType(nextRound.getType());
                        //匹配完了 进备战期吧
                        roundData.setCurRoundState(eJiuZhouRoundTime.serverMatching.getValue());
                        //重置一下下一阶段的数据准备
                        roundData.setServerDataState(eJiuZhouServerDataState.notStart.getValue());
                    }

                }
            }
        }
    }

    /**
     * 检测不同阶段的竞猜的结算
     */
    public void checkAllRoundGuessResult(){
        for (JiuZhouRoundConfig roundConfig:config.getRoundConfigMap().values()){
            if (roundConfig.getType() != eJiuZhouRoundType.knockout.getValue()){
                continue;
            }
            checkGuessEndResult(roundConfig.getId());
        }
    }


    /**
     * 检测一下竞猜的结算
     */
    public void checkGuessEndResult(int round){
        Map<Integer,Long> winnerMap = new ConcurrentHashMap<>();
        synchronized (roundDataMap){
            for (CrossJiuZhouRoundData roundData:roundDataMap.values()){
                //如果需要检测的轮次大于当前轮次 就不检测了
                if (roundData.getCurRoundId() < round){
                    continue;
                }
                //如果当前轮次不是展示期 就不检测了
                if (roundData.getCurRoundId() == round){
                    //如果当前轮次不是展示期 就不检测了
                    if (roundData.getCurRoundState() != eJiuZhouRoundTime.show.getValue()){
                        return;
                    }
                }
            }
        }
        synchronized (roundGuessDataMap){
            CrossJiuZhouGuessData crossJiuZhouGuessData = roundGuessDataMap.get(round);
            //还没到 还没生成 不管了
            if (crossJiuZhouGuessData == null){
                //logger.info("九州活动id{}竞猜数据为空,round:{}",config.getActivityId(),round);
                return;
            }
            //如果竞猜数据已经结算了 就不结算了
            Map<Integer, Long> roomWinnerMap = crossJiuZhouGuessData.getRoomWinnerMap();
            Map<Integer, List<Long>> guessResultMap = crossJiuZhouGuessData.getGuessResultMap();
            if (roomWinnerMap != null && !roomWinnerMap.isEmpty() && guessResultMap != null && !guessResultMap.isEmpty()){
                return;
            }
            Map<Integer, Integer> guessRoomIdMap = crossJiuZhouGuessData.getGuessRoomIdMap();
            //遍历一下竞猜的房间
            for (Map.Entry<Integer,Integer> entry:guessRoomIdMap.entrySet()){
                CrossJiuZhouRoomData roomData = getRoomData(entry.getValue());
                if (roomData == null){
                    logger.error("九州活动id{}竞猜房间数据为空,round:{},roomId:{}",config.getActivityId(),round,entry.getValue());
                    return;
                }
                //如果房间没有结束 就不结算了
                if (roomData.getState() != eJiuZhouRoomStateType.End.getValue()){
                    logger.error("九州活动id{}竞猜房间数据未结束,round:{},roomId:{}",config.getActivityId(),round,entry.getValue());
                    return;
                }
                //获取房间的排名
                List<JiuZhouRank> rankList = getRoomRankList(entry.getValue());
                if (rankList == null || rankList.isEmpty()){
                    logger.error("九州活动id{}竞猜房间排行榜数据为空,round:{},roomId:{}",config.getActivityId(),round,entry.getValue());
                    return;
                }
                JiuZhouRank first = rankList.get(0);
                if (first == null){
                    logger.error("九州活动id{}竞猜房间排行榜数据为空,round:{},roomId:{}",config.getActivityId(),round,entry.getValue());
                    return;
                }
                //把房间的胜利者加入到胜利者map中
                winnerMap.put(entry.getKey(),first.getUserId());
            }
            //如果胜利者map的size不等于开启的赛场数 就不结算了
            if (winnerMap.size() != config.getOpenGameZoneNum()){
                logger.error("九州活动id{}竞猜房间胜利者数量不等于3,round:{}",config.getActivityId(),round);
                return;
            }
            //如果胜利者数量和开启的赛场数量相同 就把竞猜结果写入到竞猜结果map中
            crossJiuZhouGuessData.setRoomWinnerMap(winnerMap);

            Map<Integer, List<Long>> tmpGuessResultMap = new ConcurrentHashMap<>();

            //查看猜中的人
            for (CrossJiuZhouUserGuessData guessData : userGuessDataMap.values()) {
                //获取用户的竞猜数据
                Map<Integer, Map<Integer, Long>> guessDataMap = guessData.getGuessDataMap();
                if (guessDataMap == null || guessDataMap.isEmpty()) {
                    continue;
                }
                int rightNum = 0;
                //获取当前轮次的竞猜数据
                Map<Integer, Long> guessMap = guessDataMap.get(round);
                if (guessMap == null || guessMap.isEmpty()) {
                    continue;
                }
                //用户竞猜数据与胜利者数据进行比较
                for (Map.Entry<Integer, Long> entry : guessMap.entrySet()) {
                    //如果猜中了
                    if (winnerMap.containsKey(entry.getKey()) && Objects.equals(winnerMap.get(entry.getKey()), entry.getValue())) {
                        rightNum++;
                    }
                }
                //放入猜中列表
                if (!tmpGuessResultMap.containsKey(rightNum)){
                    tmpGuessResultMap.put(rightNum,new ArrayList<>());
                }
                tmpGuessResultMap.get(rightNum).add(guessData.getUserId());
            }
            //把竞猜结果放入到轮次竞猜数据中
            crossJiuZhouGuessData.setGuessResultMap(tmpGuessResultMap);

        }
    }



    /**
     * 开始匹配
     */
    public boolean startMatching(int zoneType, JiuZhouRoundConfig nextRoundConfig) {
        int type = nextRoundConfig.getType();
        List<CrossJiuZhouUserData> needMatchingList = new ArrayList<>();
        int nextRoundId = nextRoundConfig.getId();
        for (CrossJiuZhouUserData crossJiuZhouUserData : userDataMap.values()) {
            //如果玩家是这个赛区的,并且晋级到了这一轮,就加入池子
            if (crossJiuZhouUserData.getZoneType() == zoneType && crossJiuZhouUserData.getLastRoundId() == nextRoundId) {
                //获取玩家下一个轮次的房间 如果玩家已经在房间里了,就不加入匹配池
                if (crossJiuZhouUserData.getRoomId(nextRoundId) <= 0) {
                    needMatchingList.add(crossJiuZhouUserData);
                }
            }
        }
        //按照积分从高到低排序
        needMatchingList.sort(Comparator.comparing(CrossJiuZhouUserData::getPatronsAbility).reversed());
        int playerNum = needMatchingList.size();
        if (playerNum < 2) {
            logger.error("九州活动匹配人数不足,activityId:{},zoneType:{},roundId:{}", config.getActivityInfo().getActivityId(), zoneType, nextRoundId);
            return false;
        }
        //如果不足8人,就创建一个房间塞进去
        if (playerNum <= 8) {
            CrossJiuZhouRoomData roomData = createRoomData(zoneType, nextRoundId);
            for (CrossJiuZhouUserData crossJiuZhouUserData : needMatchingList) {
                playerJoinRoom(roomData, crossJiuZhouUserData);
            }
            //万一已经insert了 保险设置一下
            roomData.setUpdateOption();
            return true;
        }
        //如果是积分赛怎么分
        if (type == eJiuZhouRoundType.score.getValue()) {

            int eightRoomNum = 0;
            int sevenRoomNum = 0;
            int noFullRoom = 0;

            //若某轮积分赛总参与人数不是8的倍数，则存在至多7个“七人赛场”,对于总人数可用二元函数表示为，总人数=8A+7B，则A为8人赛场数，B为7人赛场数。（A、B均为自然数）,若总人数无法用（8A+7B）表示，例如41、34、33等：该情况不分配7人赛场，直接把余数的玩家（1~6人）分配在一个赛场
            if (playerNum % 8 == 0) {
                eightRoomNum = playerNum / 8;
                sevenRoomNum = 0;
                noFullRoom = 0;
            } else {
                if (playerNum < 42) {
                    eightRoomNum = playerNum / 8;
                    sevenRoomNum = 0;
                    noFullRoom = 1;
                } else {
                    List<Integer> competition = competition(playerNum);
                    eightRoomNum = competition.get(0);
                    sevenRoomNum = competition.get(1);
                    noFullRoom = 0;
                }
            }
            //分房间
            boolean assignRoom = assignRoom(needMatchingList, zoneType, nextRoundConfig, eightRoomNum, sevenRoomNum, noFullRoom);
            return assignRoom;
        } else if (type == eJiuZhouRoundType.knockout.getValue()) {      // 如果是淘汰赛 又要怎么分
            int eightRoomNum = 0;
            int sevenRoomNum = 0;
            int noFullRoom = 0;
            if (playerNum < 42) {
                eightRoomNum = playerNum / 8;
                if (playerNum % 8 != 0){
                    eightRoomNum++;
                }
                sevenRoomNum = 0;
                noFullRoom = 0;
            } else {
                List<Integer> competition = competition(playerNum);
                eightRoomNum = competition.get(0);
                sevenRoomNum = competition.get(1);
                noFullRoom = 0;
            }
            boolean assignRoom = assignRoom(needMatchingList, zoneType, nextRoundConfig, eightRoomNum, sevenRoomNum, noFullRoom);
            return assignRoom;

        }

        return true;
    }

    /**
     * 分房间
     */
    public boolean assignRoom(List<CrossJiuZhouUserData> needMatchingList, int zone, JiuZhouRoundConfig nextRoundConfig, int eightNum, int sevenNum, int noFullNum) {
        //所有房间

        List<CrossJiuZhouRoomData> eightRoomList = new ArrayList<>();
        List<CrossJiuZhouRoomData> sevenRoomList = new ArrayList<>();
        List<CrossJiuZhouRoomData> noFullRoomList = new ArrayList<>();

        String matchRule = nextRoundConfig.getMatchRule();

        if (nextRoundConfig.getType() == eJiuZhouRoundType.knockout.getValue()) {     //淘汰赛
            //按照积分从高到低排序
            int playerNum = needMatchingList.size();
            if (playerNum < 2) {
                logger.error("九州活动淘汰赛匹配人数不足,activityId:{},zoneType:{},roundId:{}", config.getActivityInfo().getActivityId(), zone, nextRoundConfig.getId());
                return false;
            }
            //如果不足或者刚好8人,就创建一个房间塞进去
            if (playerNum <= 8) {
                CrossJiuZhouRoomData roomData = createRoomData(zone, nextRoundConfig.getId());
                for (CrossJiuZhouUserData crossJiuZhouUserData : needMatchingList) {
                    playerJoinRoom(roomData, crossJiuZhouUserData);
                }
                //万一已经insert了 保险设置一下
                roomData.setUpdateOption();
                return true;
            } else {
                //每种类型的房间都创建好
                for (int i = 0; i < eightNum; i++) {
                    CrossJiuZhouRoomData roomData = createRoomData(zone, nextRoundConfig.getId());
                    eightRoomList.add(roomData);
                }
                logger.debug("round:{} zone:{}总共创建了{}个八人间",nextRoundConfig.getId(),zone,eightRoomList.size());
                for (int i = 0; i < sevenNum; i++) {
                    CrossJiuZhouRoomData roomData = createRoomData(zone, nextRoundConfig.getId());
                    sevenRoomList.add(roomData);
                }
                logger.debug("round:{} zone:{}总共创建了{}个七人间",nextRoundConfig.getId(),zone,sevenRoomList.size());
                for (int i = 0; i < noFullNum; i++) {
                    CrossJiuZhouRoomData roomData = createRoomData(zone, nextRoundConfig.getId());
                    noFullRoomList.add(roomData);
                }
                logger.debug("round:{} zone:{}总共创建了{}个不满人间",nextRoundConfig.getId(),zone,noFullRoomList.size());
                //淘汰赛的第一轮 需要把前n个房间的人分配前n的人进去
                JiuZhouRoundConfig previousRoundConfigById = getJiuZhouRoundConfigById(nextRoundConfig.getId() - 1);
                if (previousRoundConfigById !=null){
                    if (previousRoundConfigById.getType() == eJiuZhouRoundType.score.getValue()){
                        addOutRoundFirst(needMatchingList, zone, nextRoundConfig, eightRoomList);
                        addOutRoundFirst(needMatchingList, zone, nextRoundConfig, sevenRoomList);
                        addOutRoundFirst(needMatchingList, zone, nextRoundConfig, noFullRoomList);
                    }
                }

                //分配其他人
                //先打乱玩家顺序
                Collections.shuffle(needMatchingList);
                //8人间塞满
                for (CrossJiuZhouRoomData roomData : eightRoomList) {
                    while (roomData.getUserNum() < 8 && needMatchingList.size() > 0) {
                        CrossJiuZhouUserData crossJiuZhouUserData = needMatchingList.get(0);
                        playerJoinRoom(roomData, crossJiuZhouUserData);
                        needMatchingList.remove(crossJiuZhouUserData);
                    }
                }
                //7人间塞满
                for (CrossJiuZhouRoomData roomData : sevenRoomList) {
                    while (roomData.getUserNum() < 7 && needMatchingList.size() > 0) {
                        CrossJiuZhouUserData crossJiuZhouUserData = needMatchingList.get(0);
                        playerJoinRoom(roomData, crossJiuZhouUserData);
                        needMatchingList.remove(crossJiuZhouUserData);
                    }
                }
                //最后不满的房间塞满
                for (CrossJiuZhouRoomData roomData : noFullRoomList) {
                    while (roomData.getUserNum() < 8 && needMatchingList.size() > 0) {
                        CrossJiuZhouUserData crossJiuZhouUserData = needMatchingList.get(0);
                        playerJoinRoom(roomData, crossJiuZhouUserData);
                        needMatchingList.remove(crossJiuZhouUserData);
                    }
                }
                //结束结束
                return true;
            }
        } else {    //积分赛怎么分
            //玩家前千分比；每个赛场人数|……|玩家前千分比；每个赛场人数|……|玩家前千分比；每个赛场人数
            //没配置千分比的剩余玩家自动补充到赛场，每个赛场人数配置值不会超过赛场上限人数
            //125;1|375;2|625;2
            List<String> strings = StringUtils.stringToStringList(matchRule, "\\|");
            if (strings.size() == 0) {
                logger.error("九州棋局积分赛配置有误 activityId:{},zoneType:{},roundId:{}", config.getActivityInfo().getActivityId(), zone, nextRoundConfig.getId());
                return false;
            }
            Map<Integer, Integer> ruleMap = new ConcurrentHashMap<>();
            for (String string : strings) {
                List<String> strings1 = StringUtils.stringToStringList(string, ";");
                if (strings1.size() != 2) {
                    logger.error("九州棋局积分赛配置有误 activityId:{},zoneType:{},roundId:{}", config.getActivityInfo().getActivityId(), zone, nextRoundConfig.getId());
                    return false;
                }
                ruleMap.put(Integer.parseInt(strings1.get(0)), Integer.parseInt(strings1.get(1)));
            }
            //每种类型的房间都创建好
            for (int i = 0; i < eightNum;i++){
                CrossJiuZhouRoomData roomData = createRoomData(zone, nextRoundConfig.getId());
                eightRoomList.add(roomData);
            }
            for (int i = 0; i < sevenNum;i++){
                CrossJiuZhouRoomData roomData = createRoomData(zone, nextRoundConfig.getId());
                sevenRoomList.add(roomData);
            }
            for (int i = 0; i < noFullNum;i++){
                CrossJiuZhouRoomData roomData = createRoomData(zone, nextRoundConfig.getId());
                noFullRoomList.add(roomData);
            }


            Map<Integer,List<CrossJiuZhouUserData>> stageMap = new ConcurrentHashMap<>();
            int size = needMatchingList.size();
            //各个排名段的人数拆开
            List<Integer> levels = new ArrayList<>(ruleMap.keySet());
            //从小到大排序levels
            Collections.sort(levels);
            int lastRank = 0;
            for (int rankPercent: levels){
                int maxRank = BigDecimal.valueOf(size).multiply(BigDecimal.valueOf(rankPercent)).divide(BigDecimal.valueOf(1000), 0, RoundingMode.UP).intValue();
                //取出这个排名段的人
                List<CrossJiuZhouUserData> crossJiuZhouUserData = new ArrayList<>(needMatchingList.subList(lastRank, maxRank));
                //更新lastRank
                lastRank = maxRank;
                //先随机一下
                Collections.shuffle(crossJiuZhouUserData);
                //扔到map里
                stageMap.put(rankPercent, crossJiuZhouUserData);
            }
            //分配到各个房间

            for (int level : levels) {
                //该排名段需要的人数
                int needNum = ruleMap.get(level);
                //8人间加入需要的人数
                for (CrossJiuZhouRoomData roomData : eightRoomList) {
                     for (int i = 0; i < needNum;i++){
                         List<CrossJiuZhouUserData> thisStageUser = stageMap.get(level);
                         if (roomData.getUserNum() < 8 && thisStageUser.size() > 0) {
                             CrossJiuZhouUserData crossJiuZhouUserData = thisStageUser.get(0);
                             playerJoinRoom(roomData, crossJiuZhouUserData);
                             thisStageUser.remove(crossJiuZhouUserData);
                             needMatchingList.remove(crossJiuZhouUserData);
                         }
                     }
                }
                //7人间加入需要的人数
                for (CrossJiuZhouRoomData roomData : sevenRoomList) {
                    for (int i = 0; i < needNum;i++){
                        List<CrossJiuZhouUserData> thisStageUser = stageMap.get(level);
                        if (roomData.getUserNum() < 7 && thisStageUser.size() > 0) {
                            CrossJiuZhouUserData crossJiuZhouUserData = thisStageUser.get(0);
                            playerJoinRoom(roomData, crossJiuZhouUserData);
                            thisStageUser.remove(crossJiuZhouUserData);
                            needMatchingList.remove(crossJiuZhouUserData);
                        }
                    }
                }
                //不满的房间加入需要的人数
                for (CrossJiuZhouRoomData roomData : noFullRoomList) {
                    for (int i = 0; i < needNum;i++){
                        List<CrossJiuZhouUserData> thisStageUser = stageMap.get(level);
                        if (roomData.getUserNum() < 8 && thisStageUser.size() > 0) {
                            CrossJiuZhouUserData crossJiuZhouUserData = thisStageUser.get(0);
                            playerJoinRoom(roomData, crossJiuZhouUserData);
                            thisStageUser.remove(crossJiuZhouUserData);
                            needMatchingList.remove(crossJiuZhouUserData);
                        }
                    }
                }
            }
            //排名的已经分配完了，剩下的人随机分配到房间
            Collections.shuffle(needMatchingList);
            for (CrossJiuZhouRoomData roomData : eightRoomList) {
                while (roomData.getUserNum() < 8 && needMatchingList.size() > 0) {
                    CrossJiuZhouUserData crossJiuZhouUserData = needMatchingList.get(0);
                    playerJoinRoom(roomData, crossJiuZhouUserData);
                    needMatchingList.remove(crossJiuZhouUserData);
                }
            }
            for (CrossJiuZhouRoomData roomData : sevenRoomList) {
                while (roomData.getUserNum() < 7 && needMatchingList.size() > 0) {
                    CrossJiuZhouUserData crossJiuZhouUserData = needMatchingList.get(0);
                    playerJoinRoom(roomData, crossJiuZhouUserData);
                    needMatchingList.remove(crossJiuZhouUserData);
                }
            }
            for (CrossJiuZhouRoomData roomData : noFullRoomList) {
                while (roomData.getUserNum() < 8 && needMatchingList.size() > 0) {
                    CrossJiuZhouUserData crossJiuZhouUserData = needMatchingList.get(0);
                    playerJoinRoom(roomData, crossJiuZhouUserData);
                    needMatchingList.remove(crossJiuZhouUserData);
                }
            }
            return true;

        }

    }

    /**
     * 把前n个人分配到前n个房间的方法
     *
     * @param needMatchingList
     * @param zone
     * @param nextRoundConfig
     * @param sevenRoomList
     */

    private void addOutRoundFirst(List<CrossJiuZhouUserData> needMatchingList, int zone, JiuZhouRoundConfig nextRoundConfig, List<CrossJiuZhouRoomData> sevenRoomList) {
        for (CrossJiuZhouRoomData roomData : sevenRoomList) {
            if (needMatchingList.size() == 0) {
                logger.error("九州棋局活动房间创建后,匹配人数不足,activityId:{},zoneType:{},roundId:{}", config.getActivityInfo().getActivityId(), zone, nextRoundConfig.getId());
                break;
            }
            CrossJiuZhouUserData crossJiuZhouUserData = needMatchingList.get(0);
            playerJoinRoom(roomData, crossJiuZhouUserData);
            needMatchingList.remove(0);
        }
    }

    /**
     * 锁定当前赛区,当前轮次的竞猜数据
     */
    public boolean lockCrossGuessData(JiuZhouRoundConfig nextRoundConfig,int zoneType){
        synchronized (roundGuessDataMap){
            CrossJiuZhouGuessData crossJiuZhouGuessData = roundGuessDataMap.get(nextRoundConfig.getId());
            if (crossJiuZhouGuessData == null){
                crossJiuZhouGuessData = new CrossJiuZhouGuessData(config.getActivityId(),nextRoundConfig.getId());
                crossJiuZhouGuessData.setInsertOption();
                roundGuessDataMap.put(nextRoundConfig.getId(),crossJiuZhouGuessData);
            }
            //各个赛区的竞猜房间id map
            Map<Integer, Integer> guessRoomIdMap = crossJiuZhouGuessData.getGuessRoomIdMap();
            if (guessRoomIdMap == null){
                guessRoomIdMap = new ConcurrentHashMap<>();
                crossJiuZhouGuessData.setGuessRoomIdMap(guessRoomIdMap);
            }
            //各个赛区的房间内玩家被全体竞猜人数map
            Map<Integer, Map<Long, Integer>> allUserGuessNum = crossJiuZhouGuessData.getAllUserGuessNum();
            if (allUserGuessNum == null){
                allUserGuessNum = new ConcurrentHashMap<>();
                crossJiuZhouGuessData.setAllUserGuessNum(allUserGuessNum);
            }
            //已经锁定了竞猜数据 竞猜房间id不为0 且 全体竞猜人数map不为空
            if (guessRoomIdMap.containsKey(zoneType) && guessRoomIdMap.get(zoneType) != 0
                    && allUserGuessNum.containsKey(zoneType) && allUserGuessNum.get(zoneType) != null){
                return true;
            }
            //最小方差和对应房间
            BigDecimal minStandardDeviation = null;
            CrossJiuZhouRoomData minRoomData = null;

            // 找到当前赛区,当前轮次的房间
            for (CrossJiuZhouRoomData roomData : roomDataMap.values()) {
                if (roomData.getZone() == zoneType && roomData.getRound() == nextRoundConfig.getId()) {
                    BigDecimal roomUserSpeedStandardDeviation = getRoomUserSpeedStandardDeviation(roomData);
                    //如果没有最小方差,或者当前房间的方差小于最小方差,则替换
                    if (minStandardDeviation == null || roomUserSpeedStandardDeviation.compareTo(minStandardDeviation) < 0) {
                        minStandardDeviation = roomUserSpeedStandardDeviation;
                        minRoomData = roomData;
                    } else if (roomUserSpeedStandardDeviation.compareTo(minStandardDeviation) == 0){ //如果方差相等,取人数多的 应该不可能吧
                        if (roomData.getUserNum() > minRoomData.getUserNum()){
                            minStandardDeviation = roomUserSpeedStandardDeviation;
                            minRoomData = roomData;
                        }
                    }
                }
            }
            //如果没有房间
            if (minRoomData == null){
                logger.error("九州棋局活动房间创建后,没有找到对应的房间,activityId:{},zoneType:{},roundId:{}", config.getActivityInfo().getActivityId(), zoneType, nextRoundConfig.getId());
                return false;
            }
            Map<Long,Integer> guessData = new ConcurrentHashMap<>();
            //把房间内的玩家id放入竞猜数据中
            minRoomData.getRoomUserIdList().forEach(userId -> {
                CrossJiuZhouUserData userData = getUserData(userId);
                if (userData != null){
                    guessData.put(userId,0);
                }
            });
            //锁定竞猜数据
            guessRoomIdMap.put(zoneType,minRoomData.getRoomId());
            allUserGuessNum.put(zoneType,guessData);

            return true;
        }
    }

    /**
     * 计算房间内玩家门客赚速的标准差
     */
    public BigDecimal getRoomUserSpeedStandardDeviation(CrossJiuZhouRoomData roomData) {
        List<BigInteger> speedList = new ArrayList<>();
        for (long userId : roomData.getRoomUserIdList()) {
            CrossJiuZhouUserData userData = getUserData(userId);
            if (userData != null) {
                speedList.add(userData.getPatronsAbility());
            }
        }
        return MathHelper.calcVariance(speedList);
    }



    /**
     * 分赛场 只有42人以上可以调用这个方法 不然会死
     *
     * @param total
     * @return
     */
    public List<Integer> competition(int total) {
        List<Integer> list = new ArrayList<>();

        int copy = total;

        int eight = 0;
        int seven = 0;

        while (total > 0) {
            if (total % 8 == 0) {
                eight += total / 8;
                total = 0;
            } else {
                total -= 7;
                seven++;
            }
        }

        list.add(eight);
        list.add(seven);
        //logger.debug("total:{},eight:{},seven:{}",total,eight,seven);
        if (eight * 8 + seven * 7 != copy) {
            logger.error("分赛场出错了,total:{},eight:{},seven:{}", total, eight, seven);
        }
        return list;
    }


    /**
     * 玩家加入房间
     */
    public void playerJoinRoom(CrossJiuZhouRoomData roomData, CrossJiuZhouUserData crossJiuZhouUserData) {
        int roomId = crossJiuZhouUserData.getRoomId(roomData.getRound());
        if (roomId > 0) {
            if (roomId != roomData.getRoomId()) {
                logger.error("九州棋局活动玩家加入房间时,玩家已经在房间里了,activityId:{},zoneType:{},roomId:{},userId:{}", config.getActivityInfo().getActivityId(), roomData.getZone(), roomData.getRoomId(), crossJiuZhouUserData.getUserId());
                return;
            }
        }

        roomData.addRoomUser(crossJiuZhouUserData.getUserId());
        crossJiuZhouUserData.addRoomId(roomData.getRound(), roomData.getRoomId());
//        logger.debug("玩家加入房间,user:{},room:{}", crossJiuZhouUserData.getUserId(), roomData.getRoomId());
        // 判断下是不是淘汰赛，淘汰赛需要更新人气榜单
        JiuZhouRoundConfig roundConfig = config.getRoundConfigMap().get(roomData.getRound());
        if (roundConfig.getType() == eJiuZhouRoundType.knockout.getValue()) {
            popularityRankChange(roomData.getRound(), crossJiuZhouUserData);
        }
    }

    /**
     * 进入下一个阶段玩家资格锁定
     */
    public boolean lockRoundUser(int zoneType, JiuZhouRoundConfig thisRoundConfig, JiuZhouRoundConfig nextRoundConfig) {
        if (nextRoundConfig == null) {
            logger.error("九州活动下一轮配置不存在,activityId:{},zoneType:{}", config.getActivityInfo().getActivityId(), zoneType);
            return false;
        }
        int count = 0;
        //第一轮?
        if (nextRoundConfig.getId() == 1) {
            //第一轮不需要锁定 检查是否所有玩家都有锁定到第一轮
            for (CrossJiuZhouUserData userData : userDataMap.values()) {
                if (userData.getLastRoundId() == 0 && userData.getZoneType() == zoneType) {
                    userData.setLastRoundId(1);
                    count++;
                }
            }
            if (count == 0){
                logger.error("九州棋局活动进入下一轮时,没有玩家锁定到下一轮,activityId:{},zoneType:{}", config.getActivityInfo().getActivityId(), zoneType);
                return false;
            }
            addRoundLockNum(zoneType, 1, count);
            return true;
        }
        int upRank = thisRoundConfig.getUpRankNum(zoneType);
        CrossJiuZhouRankListModel rankListModel;
        if (thisRoundConfig.getType() == eJiuZhouRoundType.score.getValue()) {
            rankListModel = getRankListByRoundAndType(zoneType, eJiuZhouRoundType.score.getValue());
        } else {
            rankListModel = getRankListByRoundAndType(zoneType, eJiuZhouRoundType.knockout.getValue());
        }
        if (rankListModel == null) {
            logger.error("九州活动排行榜不存在,activityId:{},zoneType:{}", config.getActivityInfo().getActivityId(), zoneType);
            return false;
        }
        // 需要同步的用户
        List<Long> needSyncUserIdList = new ArrayList<>();
        int rankNum = 1;
        //获取排行榜
        for (JiuZhouRank rank : rankListModel.getRankList()) {
            //如果锁定人数达到rank人数,就不再锁定
            if (rankNum > upRank) {
                break;
            }
            count++;
            CrossJiuZhouUserData userData = userDataMap.get(rank.getUserId());
            if (userData == null) {
                logger.error("九州活动玩家数据不存在,activityId:{},userId:{}", config.getActivityInfo().getActivityId(), rank.getUserId());
                continue;
            }
            // 判断下门客
            if (userData.getPatronsDataMap().size() < config.getSighLimit()) {
                logger.error("九州活动玩家门客数量不符合上阵条件,activityId:{},userId:{}", config.getActivityInfo().getActivityId(), rank.getUserId());
                continue;
            }
            //设置玩家锁定的轮次为下一轮
            userData.setLastRoundId(nextRoundConfig.getId());


            userData.setUpdateOption();
            //排名+1
            rankNum++;

            needSyncUserIdList.add(userData.getUserId());
        }
        if (count == 0){
            logger.error("九州棋局活动进入下一轮时,没有玩家锁定到下一轮,activityId:{},zoneType:{}", config.getActivityInfo().getActivityId(), zoneType);
            return false;
        }
        addRoundLockNum(zoneType, nextRoundConfig.getId(), count);

        for (Long userId : needSyncUserIdList) {
            // 同步下数据到区服
            crossUserDataSync(userId);
        }
        return true;
    }

    /**
     * 添加轮次锁定人数信息
     */
    public void addRoundLockNum(int zoneId,int roundId,int num){
        CrossJiuZhouRoundData roundData = roundDataMap.get(zoneId);
        if (roundData == null){
            logger.error("九州活动轮次数据不存在,activityId:{},zoneId:{},人数锁定{}失败", config.getActivityInfo().getActivityId(), zoneId,num);
            return;
        }
        Map<Integer, Integer> roundPromotionNumMap = roundData.getRoundPromotionNumMap();
        if (roundPromotionNumMap == null){
            roundData.setRoundPromotionNumMap(new ConcurrentHashMap<>());
            roundPromotionNumMap = roundData.getRoundPromotionNumMap();
        }
        roundPromotionNumMap.put(roundId,num);
        roundData.setUpdateOption();
    }


    /**
     * 获取某个赛区的排行榜
     */
    public CrossJiuZhouRankListModel getRankListByRoundAndType(int zoneType, int roundType) {
        if (roundType == eJiuZhouRoundType.score.getValue()) {
            return scoreRoundRankMap.get(zoneType);
        } else if (roundType == eJiuZhouRoundType.knockout.getValue()) {
            return outRoundRankMap.get(zoneType);
        } else {
            logger.error("轮次类型不存在");
            return null;
        }
    }


    /**
     * 游戏服上传的锁定的赚速
     */
    public void addGameUploadAbility(int serverId, List<String> abilityList) {
        logger.info("九州活动游戏服上传赚钱,serverId:{},abilityList size:{}", serverId, abilityList.size());
        List<BigInteger> earnList = new ArrayList<>();
        for (String earnSpeed : abilityList) {
            earnList.add(new BigInteger(earnSpeed));
        }
        //锁一下 防止赚速计算到一半还添加了
        synchronized (roundDataMap){
            for (CrossJiuZhouRoundData roundData : roundDataMap.values()){
                if (roundData.getIsLockAbility() != 0){
                    logger.error("九州活动游戏服上传赚钱时,已经有一个以上的区域锁定赚速,不更新了,serverId:{}", serverId);
                    return;
                }
            }
        }

        synchronized (userAbilityMap) {
            userAbilityMap.put(serverId, earnList);
        }
    }


    /**
     * 获取当前锁定赚速 如果没有这个赛区 暂定发0下去 后续看看
     */
    public int getZoneLockAbilityLimit(long userId, long serverId) {
        if (roundDataMap == null || roundDataMap.isEmpty()) {
            return GameErrorCode.E_JIU_ZHOU_ACTIVITY_DATA_PREPARING;
        }
        JiuZhouProto.JiuZhouClientGetAbilityLimitRespMsg.Builder builder = JiuZhouProto.JiuZhouClientGetAbilityLimitRespMsg.newBuilder();

        CrossJiuZhouRoundData eliteRoundData = roundDataMap.get(eJiuZhouGameZoneType.elite.getValue());
        if (eliteRoundData == null || eliteRoundData.getIsLockAbility() == 0) {
            if (config.isGameZoneOpen(eJiuZhouGameZoneType.elite.getValue())){
                return GameErrorCode.E_JIU_ZHOU_ACTIVITY_DATA_PREPARING;
            }
//            builder.addAbilityLimit("0");
        }else {
            builder.addAbilityLimit(eliteRoundData.getLockAbility().toString());
        }


        CrossJiuZhouRoundData masterRoundData = roundDataMap.get(eJiuZhouGameZoneType.master.getValue());
        if (masterRoundData == null || masterRoundData.getIsLockAbility() == 0) {
            if (config.isGameZoneOpen(eJiuZhouGameZoneType.master.getValue())){
                return GameErrorCode.E_JIU_ZHOU_ACTIVITY_DATA_PREPARING;
            }
//            builder.addAbilityLimit("0");
        }else {
            builder.addAbilityLimit(masterRoundData.getLockAbility().toString());
        }


        CrossJiuZhouRoundData kingRoundData = roundDataMap.get(eJiuZhouGameZoneType.king.getValue());
        if (kingRoundData == null || kingRoundData.getIsLockAbility() == 0) {
            if(config.isGameZoneOpen(eJiuZhouGameZoneType.king.getValue())){
                return GameErrorCode.E_JIU_ZHOU_ACTIVITY_DATA_PREPARING;
            }
//            builder.addAbilityLimit("0");
        }else {
            builder.addAbilityLimit(kingRoundData.getLockAbility().toString());
        }

        builder.setRet(0);
        sendPacket(serverId, userId, YanQuMessageUtils.buildMessage(ClientProtocol.U_JIU_ZHOU_GET_ZONE_ABILITY_LIMIT, builder));

        return 0;
    }

    /**
     * 使用商会评定等级进行报名
     */
    public int signUp(long userId, int grade,BigInteger nowMaxAbility, List<CrossJiuZhouPatronsData> patronsDataList){
        if (roundDataMap == null || roundDataMap.isEmpty()) {
            return GameErrorCode.E_JIU_ZHOU_ACTIVITY_DATA_PREPARING;
        }
        int canMaxJoinZone = config.getCanJoinZoneIdByMonthRankGrade(grade);
        //没分区
        if (canMaxJoinZone == 0) {
            return GameErrorCode.E_JIU_ZHOU_SIGN_NO_REACH_LIMIT;
        }
        //没初始化这个分区 但是玩家申请上来了 也不行
        if (!roundDataMap.containsKey(canMaxJoinZone)){
            return GameErrorCode.E_JIU_ZHOU_CONFIG_NOT_EXIST;
        }
        CrossJiuZhouRoundData roundData = roundDataMap.get(canMaxJoinZone);
        //如果开了不给
        if (roundData.getCurRoundId() != 0) {
            return GameErrorCode.E_JIU_ZHOU_ACTIVITY_HAS_START;
        }
        //没锁定赚速 不给
        if (roundData.getIsLockAbility() <= 0) {
            return GameErrorCode.E_JIU_ZHOU_ACTIVITY_DATA_PREPARING;
        }
        //赚速没达到不给
        if (roundData.getLockAbility().compareTo(nowMaxAbility) > 0){
            return GameErrorCode.E_JIU_ZHOU_SIGN_NO_REACH_LIMIT;
        }

        //门客不足
        if (patronsDataList.size() < config.getSighLimit()) {
            return GameErrorCode.E_JIU_ZHOU_SIGN_NO_REACH_LIMIT;
        }
        //有用户数据就是已经报名了吧 如果出现跨服本服不为空要根据特定id去更新本服的状态吗
//        if (getUserData(userId) != null){
//            return GameErrorCode.E_JIU_ZHOU_HAS_SIGN_UP;
//        }
        //初始化
        CrossJiuZhouUserData userData = initUserData(userId);
        if (userData == null) {
            return GameErrorCode.E_JIU_ZHOU_ACTIVITY_INIT_DATA_ERROR;
        }
        // 赚钱
        userData.setPatronsAbility(nowMaxAbility);

        Map<Integer, CrossJiuZhouPatronsData> patronsDataMap = new ConcurrentHashMap<>();
        for (CrossJiuZhouPatronsData patronsData : patronsDataList) {
            patronsDataMap.put(patronsData.getPatronsId(), patronsData);
        }
        //  更新门客数据
        userData.setPatronsDataMap(patronsDataMap);

        // 不允许变更赛区
        if (userData.getZoneType() == 0) {
            userData.setZoneType(canMaxJoinZone);
        }
        userData.setUpdateOption();

        CrossAutoLogMgr.add(new LogJiuZhouSignUp(config.getActivityId(),userId,grade,nowMaxAbility,userData.getZoneType(),new Date()));

        JiuZhouProto.SignUpJiuZhouRespMsg.Builder builder = JiuZhouProto.SignUpJiuZhouRespMsg.newBuilder();
        builder.setRet(0);
        JiuZhouProto.JiuZhouCrossUserDataMsg.Builder userDataMsg = parseJiuZhouCrossUserDataMsg(userId, 0);
        builder.setCrossUserData(userDataMsg);
        sendPacket(Protocol.S_JIU_ZHOU_SIGN_UP_FROM_CROSS, builder, userId);
        logger.info("报名成功，当前报名人数:{}", userDataMap.size());

        // 同步下数据到区服
        crossUserDataSync(userId);
        return 0;

    }


    /**
     * 报名 2023 12 16 策划说用实力评定 弃用此方法
     */
    @Deprecated
    public int signUp(long userId, BigInteger nowMaxAbility, List<CrossJiuZhouPatronsData> patronsDataList) {
        if (roundDataMap == null || roundDataMap.isEmpty()) {
            return GameErrorCode.E_JIU_ZHOU_ACTIVITY_DATA_PREPARING;
        }
        //可以加入的轮次 越大越牛
        int canMaxJoinZone = 0;

        for (CrossJiuZhouRoundData roundData : roundDataMap.values()) {
            if (roundData.getIsLockAbility() <= 0) {
                return GameErrorCode.E_JIU_ZHOU_ACTIVITY_DATA_PREPARING;
            }
            if (roundData.getCurRoundId() != 0) {
                return GameErrorCode.E_JIU_ZHOU_ACTIVITY_HAS_START;
            }
            if (roundData.getZoneType() > canMaxJoinZone && nowMaxAbility.compareTo(roundData.getLockAbility()) >= 0) {
                canMaxJoinZone = roundData.getZoneType();
            }
        }
        if (canMaxJoinZone == 0) {
            return GameErrorCode.E_JIU_ZHOU_SIGN_NO_REACH_LIMIT;
        }
        if (patronsDataList.size() < config.getSighLimit()) {
            return GameErrorCode.E_JIU_ZHOU_SIGN_NO_REACH_LIMIT;
        }
        //有用户数据就是已经报名了吧 如果出现跨服本服不为空要根据特定id去更新本服的状态吗
//        if (getUserData(userId) != null){
//            return GameErrorCode.E_JIU_ZHOU_HAS_SIGN_UP;
//        }

        CrossJiuZhouUserData userData = initUserData(userId);
        if (userData == null) {
            return GameErrorCode.E_JIU_ZHOU_ACTIVITY_INIT_DATA_ERROR;
        }
        // 赚钱
        userData.setPatronsAbility(nowMaxAbility);

        Map<Integer, CrossJiuZhouPatronsData> patronsDataMap = new ConcurrentHashMap<>();
        for (CrossJiuZhouPatronsData patronsData : patronsDataList) {
            patronsDataMap.put(patronsData.getPatronsId(), patronsData);
        }
        //  更新门客数据
        userData.setPatronsDataMap(patronsDataMap);

        // 不允许变更赛区
        if (userData.getZoneType() == 0) {
            userData.setZoneType(canMaxJoinZone);
        }
        userData.setUpdateOption();

        CrossAutoLogMgr.add(new LogJiuZhouSignUp(config.getActivityId(),userId,0,nowMaxAbility,userData.getZoneType(),new Date()));

        JiuZhouProto.SignUpJiuZhouRespMsg.Builder builder = JiuZhouProto.SignUpJiuZhouRespMsg.newBuilder();
        builder.setRet(0);
        JiuZhouProto.JiuZhouCrossUserDataMsg.Builder userDataMsg = parseJiuZhouCrossUserDataMsg(userId, 0);
        builder.setCrossUserData(userDataMsg);
        sendPacket(Protocol.S_JIU_ZHOU_SIGN_UP_FROM_CROSS, builder, userId);
        logger.info("报名成功，当前报名人数:{}", userDataMap.size());

        return 0;

    }


    /**
     * 检测各个服务器上传的玩家赚速数据
     */
    public void checkServerUploadData(CrossJiuZhouRoundData roundData) {
        ActivityInfo activityInfo = getConfig().getActivityInfo();
        if (activityInfo == null) {
            logger.error("九州棋局活动信息不存在,活动id:{}", roundData.getActivityId());
            return;
        }
        boolean needCheck = false;
        //遍历轮次信息 看看有没有必要锁
        for (CrossJiuZhouRoundData jiuZhouRoundData:roundDataMap.values()){
            if (jiuZhouRoundData.getIsLockAbility() == 0){
                needCheck = true;
                break;
            }
        }

        if(!needCheck){
            return;
        }
        //是否可以开始锁定
        boolean canStartLock = true;
        List<Long> serverIdList = activityInfo.getServerIdList();
        for (long serverId : serverIdList) {
            int intServerId = (int) serverId;
            if (userAbilityMap.containsKey(intServerId)) {
                if (userAbilityMap.get(intServerId).isEmpty()){
                    //是空的也下去在拿一次 防止数据错
                    notifyServerToUpload(serverId);
                }
                //不管是不是空 都可以锁 只要上传过
                continue;
            } else {
                //还有区服没上来 不能锁
                canStartLock = false;
                notifyServerToUpload(serverId);
            }
        }
        if (canStartLock) {
            List<BigInteger> totalEarnList = new ArrayList<>();

            for (List<BigInteger> earnList : userAbilityMap.values()) {
                totalEarnList.addAll(earnList);
            }
            totalEarnList.sort(new Comparator<BigInteger>() {
                @Override
                public int compare(BigInteger o1, BigInteger o2) {
                    return o2.compareTo(o1);
                }
            });

            if (ServerListMgr.getChannelId(CrossBattleServer.getInstance().getServerId()) == 666){
//                logger.error("九州棋局活动上传赚速数据 详细赚速为! 活动id{}",config.getActivityId());
//                totalEarnList.forEach(s -> logger.error("赚速:"+s));
            }
            List<Integer> zoneCondition = config.getZoneCondition();
            if (zoneCondition == null || zoneCondition.size() < 3) {
                logger.error("九州棋局活动区服报名条件配置错误,活动id:{}", roundData.getActivityId());
                return;
            }
            int earnSpeedLimitPercent = 0;
            if (roundData.getZoneType() == eJiuZhouGameZoneType.king.getValue()) {
                earnSpeedLimitPercent = zoneCondition.get(0);
            } else if (roundData.getZoneType() == eJiuZhouGameZoneType.master.getValue()) {
                earnSpeedLimitPercent = zoneCondition.get(1);
            } else if (roundData.getZoneType() == eJiuZhouGameZoneType.elite.getValue()){
                earnSpeedLimitPercent = zoneCondition.get(2);
            }
            //如果锁的人十个都没有！
            if (totalEarnList.size() <= 10){
                BigInteger earnSpeedLimit = BigInteger.ZERO;
                if (roundData.getZoneType() == eJiuZhouGameZoneType.king.getValue()){
                    earnSpeedLimit = BigInteger.valueOf(100000000L);
                }else if (roundData.getZoneType() == eJiuZhouGameZoneType.master.getValue()){
                    earnSpeedLimit = BigInteger.valueOf(100000000L);
                }else if (roundData.getZoneType() == eJiuZhouGameZoneType.elite.getValue()){
                    earnSpeedLimit = BigInteger.valueOf(100000000L);
                }else {
                    earnSpeedLimit = BigInteger.valueOf(0);
                }
                roundData.setLockAbility(earnSpeedLimit);
                logger.error("九州棋局活动锁定十个人都没有,只有{}人,活动id:{},赛场类型:{},赚速下限:{}", totalEarnList.size(),roundData.getActivityId(), roundData.getZoneType(), earnSpeedLimit);

            }else {
                //算一下王者赛区和大师赛区
                if (earnSpeedLimitPercent > 0 && earnSpeedLimitPercent < 1000) {
                    //取出赚钱列表的大小
                    int size = totalEarnList.size();
                    //取出赚钱列表的百分之多少的索引
                    int index = BigDecimal.valueOf(size).multiply(BigDecimal.valueOf(earnSpeedLimitPercent)).divide(BigDecimal.valueOf(1000), 0, RoundingMode.DOWN).intValue() - 1;
                    if (index > size) {
                        index = size - 1;
                    }
                    if (index < 0){
                        index = 0;
                    }
                    //取出赚钱列表的百分之多少的值
                    BigInteger earnSpeedLimit = totalEarnList.get(index);
                    //取出标准值的长度
                    String earnString = earnSpeedLimit.toString();
                    int length = earnString.length();
                    //取出需要的10的次方
                    int needTenNum = length - 2;
                    //如果需要的10的次方小于0,就设置为0
                    if (needTenNum < 0) {
                        needTenNum = 0;
                    }
                    //用BigDecimal来计算取整之后的结果
                    BigDecimal calNum = BigDecimal.valueOf(10).pow(needTenNum);
                    BigDecimal bigDecimalEarn = new BigDecimal(earnString);
                    BigDecimal resultEarn = bigDecimalEarn.divide(calNum, 0, RoundingMode.UP).multiply(calNum);
                    //转成BigInteger
                    BigInteger resultLimit = resultEarn.toBigInteger();
                    //设置赚速下限
                    roundData.setLockAbility(resultLimit);
                    logger.debug("九州棋局活动赚速下限,活动id:{},赛场类型:{},赚速下限:{}", roundData.getActivityId(), roundData.getZoneType(), resultLimit);
                } else {
                    //设置赚速下限为0
                    roundData.setLockAbility(BigInteger.ZERO);
                }
            }
            //已经锁完了
            roundData.setIsLockAbility(1);
            //设置当前阶段为报名阶段
            roundData.setCurRoundState(eJiuZhouRoundTime.prepare.getValue());
        }
    }

    /**
     * 提醒区服要上传赚速了
     */
    public void notifyServerToUpload(long serverId) {
        long now = System.currentTimeMillis();
        if (lastRequestServerTimeMap.containsKey(serverId)) {
            //5秒请求一次吧 不要太频繁
            if (now - lastRequestServerTimeMap.get(serverId) < 1000 * 5) {
                return;
            }
        }
        lastRequestServerTimeMap.put(serverId, now);

        CrossJiuZhouProto.JiuZhouGetAbilityFromGameReqMsg.Builder builder = CrossJiuZhouProto.JiuZhouGetAbilityFromGameReqMsg.newBuilder();
        builder.setActivityId(config.getActivityId());
        YanQuMessage message = YanQuMessageUtils.buildMessage(GameProtocol.S_JIU_ZHOU_CROSS_GET_GAME_ABILITY_DATA_REQ, builder);
        sendPacket(serverId, 0, message);
    }

    /**
     * 创建一个npc用户
     */
    public CrossJiuZhouUserData addNpc() {
        // 角色信息
        CrossUserBaseInfo crossNpcUserBaseInfo = CrossNpcUserMgr.createCrossNpcUserBaseInfo();

        // 来点门客,随便拿点镜像
        Map<Integer, CrossJiuZhouPatronsData> patronsDataMap = getRandomPatronsDataMap();

        // 初始化一下用户数据
        CrossJiuZhouUserData userData = initUserData(crossNpcUserBaseInfo.getUserId());
        // 门客数据
        userData.setPatronsDataMap(patronsDataMap);

        return userData;
    }

    /**
     * 随机复制一份门客数据
     */
    public Map<Integer, CrossJiuZhouPatronsData> getRandomPatronsDataMap() {
        Map<Integer, CrossJiuZhouPatronsData> map = new HashMap<>();
        // 从已有的用户中随机挑一组门客吧，测试用
        List<Long> list = new ArrayList<>();
        for (CrossJiuZhouUserData userData : userDataMap.values()) {
            if (userData == null) {
                continue;
            }
            if (CrossNpcUserMgr.isNpc(userData.getUserId())) {
                continue;
            }
            if (userData.getPatronsDataMap().isEmpty() || userData.getPatronsDataMap().size() < config.getSighLimit()) {
                continue;
            }
            list.add(userData.getUserId());
        }
        if (list.size() == 0) {
            logger.error("占无可用门客");
            return new HashMap<>();
        }
        Long userId = list.get(randomHelper.next(0, list.size()));
        CrossJiuZhouUserData userData = getUserData(userId);
        for (CrossJiuZhouPatronsData patronsData : userData.getPatronsDataMap().values()) {
            CrossJiuZhouPatronsData clone = patronsData.clone();
            map.put(clone.getPatronsId(), clone);
        }
        return map;
    }


    /**
     * 调试指令
     *
     * @param userId 用户id
     * @param code   指令
     * @param param  参数
     */
    public void debugByCmd(long userId, String code, String param) {
        CrossJiuZhouUserData userData = getUserData(userId);
        if (userData == null) {
            return;
        }
        logger.info("玩家:{},发送指令:{},参数:{}", userId, code, param);
        if (code.equals("/jzjn")) {
            // 指定锦囊
            debugSkillId3 = StringUtils.stringToIntegerList(param, "#");
        }else if (code.equals("/jzsq")) {
            // 改赛区
            userData.setZoneType(Integer.parseInt(param));
            // 同步下
            crossUserDataSync(userId);
        }else if (code.equals("/jzdebug")) {
            if (param.equals("0")) {
                debug = false;
            } else if (param.equals("1")) {
                debug = true;
            }
        } else if (code.equals("/jznpc1")) {
            //测试报名很多人
            signRandomPlayerTest(Integer.parseInt(param));
        } else if (code.equals("/jznpc2")) {
            //测试报名很多人（从已报名中随机）
            signRandomPlayerTest2(Integer.parseInt(param));
        } else if (code.equals("/jznpc3")) {
            //测试报名很多人（从已报名中随机）
            signRandomPlayerTest3(param);
        } else if (code.equals("/jzclear")) {
            userDataMap.clear();
            roomDataMap.clear();
            roundDataMap.clear();
            synchronized (roundDataMap) {
                for (eJiuZhouGameZoneType type : eJiuZhouGameZoneType.values()) {
                    if (roundDataMap.containsKey(type.getValue())) {
                        continue;
                    }
                    CrossJiuZhouRoundData roundData = new CrossJiuZhouRoundData();
                    roundData.setActivityId(getConfig().getActivityId());
                    roundData.setZoneType(type.getValue());
                    roundData.setCurRoundId(0);
                    roundData.setCurRoundState(eJiuZhouRoundTime.serverMatching.getValue());
                    roundData.setRoundType(eJiuZhouRoundType.noOpen.getValue());
                    roundData.setIsLockAbility(0);
                    roundData.setLockAbility(BigInteger.ZERO);
                    roundData.setInsertOption();
                    roundDataMap.put(type.getValue(), roundData);
                }
            }
        } else if (code.equals("/jztime")) {
            long beginTime = config.getActivityInfo().getBeginTime();
            config.getActivityInfo().setBeginTime(StringUtils.string2longSafe(param, beginTime));
        } else if (code.equals("/jzjump")) {
            if (param.equals("0")) {
                config.getActivityInfo().setBeginTime(config.getActivityInfo().getBeginShowTime());
            } else {
                List<Integer> integerList = StringUtils.stringToIntegerList(param, "-");
                int roundId = integerList.get(0);
                int roundState = integerList.get(1);
                long now = System.currentTimeMillis();
                JiuZhouRoundConfig roundConfig = config.getRoundConfigMap().get(roundId);
                if (roundConfig == null) {
                    return;
                }
                List<Integer> timeList = roundConfig.getTimeList();
                if (roundState >= timeList.size()) {
                    return;
                }
                long startTime = now - (timeList.get(roundState) * DateHelper.MINUTE_MILLIONS);
                config.getActivityInfo().setBeginTime(startTime / 1000);
            }
        } else if (code.equals("/jzuprank")) {
            List<Integer> params = StringUtils.stringToIntegerList(param, "-");
        } else if (code.equals("/jzfakeguess")) {
            List<Long> userIdList = new ArrayList<>();
            userIdList.add(101000060005101L);
            userIdList.add(101000070008504L);
            userIdList.add(101000080003672L);
            userIdList.add(101000080004163L);
            userIdList.add(101000080004360L);
            userIdList.add(101000080004885L);
            userIdList.add(101000080005623L);
            userIdList.add(101000080005904L);

            for (JiuZhouRoundConfig roundConfig : config.getRoundConfigMap().values()) {
                if (roundConfig.getType() != eJiuZhouRoundType.knockout.getValue()) {
                    continue;
                }
                CrossJiuZhouGuessData crossJiuZhouGuessData = new CrossJiuZhouGuessData(config.getActivityId(), roundConfig.getId());
                Map<Integer, Integer> guessRoomIdMap = new HashMap<>();
                Map<Integer, Map<Long, Integer>> allUserGuessNum = new HashMap<>();
                Map<Integer, List<Long>> guessResultMap = new HashMap<>();
                Map<Integer, Long> roomWinnerMap = new HashMap<>();

                guessRoomIdMap.put(1, 1);
                guessRoomIdMap.put(2, 2);
                guessRoomIdMap.put(3, 3);

                allUserGuessNum.put(1, new HashMap<>());
                allUserGuessNum.put(2, new HashMap<>());
                allUserGuessNum.put(3, new HashMap<>());

                guessResultMap.put(0, new ArrayList<>());
                guessResultMap.put(1, new ArrayList<>());
                guessResultMap.put(2, new ArrayList<>());
                guessResultMap.put(3, new ArrayList<>());


                for (Long id : userIdList) {
                    allUserGuessNum.get(1).put(id, 1);
                    allUserGuessNum.get(2).put(id, 2);
                    allUserGuessNum.get(3).put(id, 3);

                    guessResultMap.get(0).add(id);
                    guessResultMap.get(1).add(id);
                    guessResultMap.get(2).add(id);
                    guessResultMap.get(3).add(id);
                }

                for (Long id : userIdList) {
                    for (int i = 0; i < 15; i++) {
                        guessResultMap.get(0).add(id);
                        guessResultMap.get(1).add(id);
                        guessResultMap.get(2).add(id);
                        guessResultMap.get(3).add(id);
                    }
                }


                roomWinnerMap.put(1, 101000080004163L);
                roomWinnerMap.put(2, 101000080003672L);
                roomWinnerMap.put(3, 101000080005904L);

                crossJiuZhouGuessData.setRoomWinnerMap(roomWinnerMap);
                crossJiuZhouGuessData.setGuessRoomIdMap(guessRoomIdMap);
                crossJiuZhouGuessData.setAllUserGuessNum(allUserGuessNum);
                crossJiuZhouGuessData.setGuessResultMap(guessResultMap);

                roundGuessDataMap.put(roundConfig.getId(), crossJiuZhouGuessData);
            }
            return;
        } else if (code.equals("/jzfakeout")) {
            Map<Integer, JiuZhouRoundConfig> roundConfigMap = config.getRoundConfigMap();
            List<Integer> idList = new ArrayList<>(roomDataMap.keySet());
            for (JiuZhouRoundConfig jiuZhouRoundConfig : roundConfigMap.values()) {
                if (jiuZhouRoundConfig.getType() != eJiuZhouRoundType.knockout.getValue()) {
                    continue;
                }
//                if (jiuZhouRoundConfig.getUpRank() == 0) {
//                    roundRoomIdMap.put(jiuZhouRoundConfig.getId(), new ArrayList<>());
//                    roundRoomIdMap.get(jiuZhouRoundConfig.getId()).add(idList.get(0));
//                    continue;
//                }
//                int needRoom = jiuZhouRoundConfig.getUpRank() * 2 / 8;
//                roundRoomIdMap.put(jiuZhouRoundConfig.getId(), new ArrayList<>());
//                for (int i = 0; i < needRoom; i++) {
//                    roundRoomIdMap.get(jiuZhouRoundConfig.getId()).add(idList.get(i));
//                }

            }
        } else if (code.equals("/jzscore")) {

        } else if (code.equals("/jzchattest")) {
            for (CrossJiuZhouUserData tempUserData : userDataMap.values()) {
                UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(tempUserData.getUserId());
                if (userBaseInfo == null) {
                    continue;
                }
                // 封装下聊天内容
                WorldMessageProto.WorldMessageTempMsg.Builder msg = WorldMessageMgr.parseWorldMessageTempMsg(
                        userBaseInfo,
                        tempUserData.getUserId(),
                        eMessageType.CrossActivityChat.getValue(),
                        eChatContentType.Common.getContentType(),
                        "聊天测试",
                        DateHelper.getCurrentTime(),
                        config.getActivityId(),
                        ""
                );
                ActivityChatMgr.activityChat(config.getActivityId(), msg.build());
            }

        }

    }

    /**
     * 拉取活动数据
     */
    public int getActivityDataByCmd(long userId, int round, BigInteger patronsAbility) {
        CrossJiuZhouUserData userData = getUserData(userId);
        if (userData == null) {
            return GameErrorCode.E_JIU_ZHOU_USER_NOT_FOUND;
        }

        if (patronsAbility.compareTo(BigInteger.ZERO) > 0) {
            userData.setPatronsAbility(patronsAbility);
        }
        // 用户数据
        JiuZhouProto.JiuZhouDataRespMsg.Builder respMsg = JiuZhouProto.JiuZhouDataRespMsg.newBuilder();
        respMsg.setRet(0);
        respMsg.setCrossUserData(parseJiuZhouCrossUserDataMsg(userId, round));
        sendPacket(Protocol.U_JIU_ZHOU_GET_DATA, respMsg, userId);
        return 0;
    }

    /**
     * 用户数据消息
     * 支持查看任意用户任意轮次房间的数据
     */
    public JiuZhouProto.JiuZhouCrossUserDataMsg.Builder parseJiuZhouCrossUserDataMsg(long userId, int round) {
        CrossJiuZhouUserData userData = getUserData(userId);
        if (userData == null) {
            return null;
        }
        // 当前轮次
        if (round == 0) {
            round =  getCurRoundByUserId(userId);
            // 如果被淘汰了，取最后一轮
            round = Math.min(userData.getLastRoundId(), round);
        }
        JiuZhouProto.JiuZhouCrossUserDataMsg.Builder builder = JiuZhouProto.JiuZhouCrossUserDataMsg.newBuilder();
        builder.setZoneType(userData.getZoneType());
        // 当前轮次
        builder.setRound(round);
        if (userData.getRoomIdMap().containsKey(round)) {
            // 获取某轮次的用户数据
            Integer roomId = userData.getRoomIdMap().get(round);
            builder.setRoomId(roomId);
            CrossJiuZhouRoomData roomData = getRoomData(roomId);
            if (roomData != null) {
                builder.setState(roomData.getState());
                JiuZhouProto.JiuZhouRoomDataMsg.Builder roomDataMsg = parseJiuZhouRoomDataMsg(roomData, userId);
                if (roomDataMsg != null) {
                    builder.setRoomData(roomDataMsg);
                }
            }
        }
        int curRound = 0;
        CrossJiuZhouRoundData roundData = roundDataMap.get(userData.getZoneType());
        if (roundData!=null){
            curRound = roundData.getCurRoundId();
        }

        // 淘汰轮次
        builder.setOutRoundId(curRound == userData.getLastRoundId() ? 0 : userData.getLastRoundId());
        return builder;
    }

    /**
     * 房间数据消息
     *
     * @param roomData 房间数据
     * @param userId   用户id，有指定需要返回布阵数据
     */
    public JiuZhouProto.JiuZhouRoomDataMsg.Builder parseJiuZhouRoomDataMsg(CrossJiuZhouRoomData roomData, long userId) {
        if (roomData == null) {
            return null;
        }
        JiuZhouProto.JiuZhouRoomDataMsg.Builder roomTemp = JiuZhouProto.JiuZhouRoomDataMsg.newBuilder();
        roomTemp.setRound(roomData.getRound());
        roomTemp.setRoomId(roomData.getRoomId());
        roomTemp.setState(roomData.getState());
        roomTemp.setZoneType(roomData.getZone());
        // 所有玩家的数据
        CrossJiuZhouRankListModel rankListModel = roomBattleRankMap.get(roomData.getRoomId());
        for (Long targetUserId : roomData.getRoomUserIdList()) {
            CrossJiuZhouUserData targetUserData = getUserData(targetUserId);
            if (targetUserData == null) {
                continue;
            }
            // 获取轮次数据
            JiuZhouProto.JiuZhouRoomUserDataMsg.Builder roomUserTemp = JiuZhouProto.JiuZhouRoomUserDataMsg.newBuilder();
            // 获取截止这个轮次的积分
            long totalScore = 0;
            for (CrossJiuZhouUserRoundRecordData recordData : targetUserData.getRecordDataMap().values()) {
                if (roomData.getRound() < recordData.getRound()) {
                    continue;
                }
                totalScore += recordData.getScore();
            }
            roomUserTemp.setScore(totalScore);
            roomUserTemp.setUserId(targetUserId);
            // 用户信息
            CrossUserBaseInfo crossUserBaseInfo = getCrossUserBaseInfo(targetUserId);
            if (crossUserBaseInfo != null) {
                roomUserTemp.setUserBaseInfo(PlayerBasePb.parsePlayerBaseTempMsg(crossUserBaseInfo.getUserBaseInfo()));
            }
            // 房间内排名
            if (rankListModel != null) {
                roomUserTemp.setRank(rankListModel.getMyRank(targetUserId));
            } else {
                roomUserTemp.setRank(1);
            }
            roomTemp.addUserData(roomUserTemp);
        }
        // 我的布阵数据
        if (userId != 0) {
            CrossJiuZhouUserData userData = getUserData(userId);
            if (userData != null) {
                Map<Long, List<CrossJiuZhouDeployData>> listMap = deployDataMap.getOrDefault(roomData.getRoomId(), new ConcurrentHashMap<>());
                List<CrossJiuZhouDeployData> list = listMap.getOrDefault(userId, new ArrayList<>());
                for (CrossJiuZhouDeployData deployData : list) {
                    roomTemp.addDeploy(JiuZhouPb.parseJiuZhouDeployDataMsg(deployData, userData.getPatronsDataMap()));
                }
            }
            JiuZhouRoundConfig jiuZhouRoundConfigById = getJiuZhouRoundConfigById(roomData.getRound());
            if (jiuZhouRoundConfigById != null) {
                //获取我的排名
                int myRank = getRankByIdAndType(userId, roomData.getZone(), jiuZhouRoundConfigById.getType());
                roomTemp.setMyRank(myRank);
            }else {
                logger.error("九州棋局活动获取轮次配置失败,活动id:{},轮次id:{}", config.getActivityId(), roomData.getRound());
                roomTemp.setMyRank(-1);
            }

            // 剩余多少人
            CrossJiuZhouRoundData roundData = roundDataMap.get(roomData.getZone());
            if (roundData != null && roundData.getRoundPromotionNumMap().containsKey(roomData.getRound())) {
                roomTemp.setTotalNum(roundData.getRoundPromotionNumMap().get(roomData.getRound()));
            } else {
                // 容错，正常不会到这里来
                if (roomData.getRound() == 1){
                    roomTemp.setTotalNum(0);
                }else {
                    JiuZhouRoundConfig beforeRoundConfig = getJiuZhouRoundConfigById(roomData.getRound() - 1);
                    if (beforeRoundConfig != null) {
                        roomTemp.setTotalNum(beforeRoundConfig.getUpRankNum(roomData.getZone()));
                    } else {
                        roomTemp.setTotalNum(0);
                    }
                }
            }
        }
        return roomTemp;
    }

    public int getRankByIdAndType(long userId, int zoneType, int roundType) {
        CrossJiuZhouRankListModel rankListModel = getRankListByRoundAndType(zoneType, roundType);
        if (rankListModel == null) {
            return -1;
        }
        return rankListModel.getMyRank(userId);
    }



    /**
     * 获取用户信息数据
     */
    public CrossUserBaseInfo getCrossUserBaseInfo(long userId) {
        CrossUserBaseInfo crossUserBaseInfo = CrossUserMgr.getCrossUserBaseInfo(userId);
        if (crossUserBaseInfo == null) {
            // 找一下npc
            crossUserBaseInfo = CrossNpcUserMgr.getCrossNpcUserBaseInfo(userId);
        }
        return crossUserBaseInfo;
    }

    /**
     * 银两鼓舞
     *
     * @param userId
     * @param roomId
     * @param opponentUserId
     * @param cost
     */
    public int silverBuyBuffByCmd(long userId, int roomId, long opponentUserId, String cost) {
        CrossJiuZhouUserData userData = getUserData(userId);
        if (userData == null) {
            return GameErrorCode.E_JIU_ZHOU_USER_NOT_FOUND;
        }
        CrossJiuZhouRoomData roomData = getRoomData(roomId);
        if (roomData == null) {
            return GameErrorCode.E_JIU_ZHOU_ROOM_NOT_FOUND;
        }
        if (roomData.getState() != eJiuZhouRoomStateType.Deploy.getValue()) {
            return GameErrorCode.E_JIU_ZHOU_ROOM_NOT_IN_DEPLOY;
        }
        CrossJiuZhouDeployData deployData = getDeployData(roomId, userId, opponentUserId);
        if (deployData == null) {
            return GameErrorCode.E_JIU_ZHOU_DEPLOY_NOT_EXIST;
        }
        deployData.setSilverBuff(deployData.getSilverBuff().add(new BigInteger(cost)));
        // 回游戏服
        CrossJiuZhouProto.CrossJiuZhouSilverBuyBuffRespMsg.Builder builder = CrossJiuZhouProto.CrossJiuZhouSilverBuyBuffRespMsg.newBuilder();
        builder.setRet(0);
        builder.setCost(cost);
        builder.setDeploy(JiuZhouPb.parseJiuZhouDeployDataMsg(deployData, userData.getPatronsDataMap()));
        sendPacket(Protocol.S_JIU_ZHOU_SILVER_BUY_BUFF_FROM_CROSS, builder, userId);
        return 0;
    }

    /**
     * 选择棋书
     *
     * @param userId
     * @param roomId
     * @param opponentUserId
     * @param skillId2
     */
    public int chooseSkillId2ByCmd(long userId, int roomId, long opponentUserId, int skillId2) {
        CrossJiuZhouUserData userData = getUserData(userId);
        if (userData == null) {
            return GameErrorCode.E_JIU_ZHOU_USER_NOT_FOUND;
        }
        CrossJiuZhouRoomData roomData = getRoomData(roomId);
        if (roomData == null) {
            return GameErrorCode.E_JIU_ZHOU_ROOM_NOT_FOUND;
        }
        if (roomData.getState() != eJiuZhouRoomStateType.Deploy.getValue()) {
            return GameErrorCode.E_JIU_ZHOU_ROOM_NOT_IN_DEPLOY;
        }
        CrossJiuZhouDeployData deployData = getDeployData(roomId, userId, opponentUserId);
        if (deployData == null) {
            return GameErrorCode.E_JIU_ZHOU_DEPLOY_NOT_EXIST;
        }
        int oldSkillId2 = deployData.getSkillId2();
        deployData.setSkillId2(skillId2);
        CrossJiuZhouProto.CrossJiuZhouChooseSkillId2RespMsg.Builder builder = CrossJiuZhouProto.CrossJiuZhouChooseSkillId2RespMsg.newBuilder();
        // 回游戏服
        builder.setRet(0);
        builder.setOldSkillId2(oldSkillId2);
        builder.setNewSkillId2(deployData.getSkillId2());
        builder.setDeploy(JiuZhouPb.parseJiuZhouDeployDataMsg(deployData, userData.getPatronsDataMap()));
        sendPacket(Protocol.S_JIU_ZHOU_CHOOSE_SKILLID2_FROM_CROSS, builder, userId);
        return 0;
    }

    /**
     * 变更用户门客数据
     *
     * @param userId          用户id
     * @param patronsDataList 门客数据
     */
    public void changeUserPatronsList(long userId, List<PatronsProto.PatronsSimpleTempMsg> patronsDataList) {
        CrossJiuZhouUserData userData = getUserData(userId);
        if (userData == null) {
            return;
        }
        CrossJiuZhouRoundData roundData = roundDataMap.get(userData.getZoneType());
        if (roundData == null) {
            return;
        }
        // 战斗期无法变更门客
        if (roundData.getCurRoundState() == eJiuZhouRoundTime.fight.getValue()) {
            return;
        }
        for (PatronsProto.PatronsSimpleTempMsg tempMsg : patronsDataList) {
            CrossJiuZhouPatronsData patronsData = JiuZhouPb.parseCrossJiuZhouPatronsData(tempMsg);
            if (patronsData == null) {
                continue;
            }
            // 更新门客
            userData.changePatronsData(patronsData);
        }
        logger.info("九州棋局门客数据变更,用户:{}", userId);
    }

    /**
     * 门客上阵
     *
     * @param userId         用户id
     * @param roomId         房间id
     * @param opponentUserId 对手id
     * @param patronsIdList  门客数据
     */
    public int choosePatronsByCmd(long userId, int roomId, long opponentUserId, List<Integer> patronsIdList) {
        CrossJiuZhouUserData userData = getUserData(userId);
        if (userData == null) {
            return GameErrorCode.E_JIU_ZHOU_USER_NOT_FOUND;
        }
        CrossJiuZhouRoomData roomData = getRoomData(roomId);
        if (roomData == null) {
            return GameErrorCode.E_JIU_ZHOU_ROOM_NOT_FOUND;
        }
        if (roomData.getState() != eJiuZhouRoomStateType.Deploy.getValue()) {
            return GameErrorCode.E_JIU_ZHOU_ROOM_NOT_IN_DEPLOY;
        }
        // 判断其他门客的上阵情况
        List<CrossJiuZhouDeployData> deployDataList = getDeployDataList(roomId, userId);

        for (CrossJiuZhouDeployData deployData : deployDataList) {
            if (deployData.getOpponentUserId() == opponentUserId) {
                deployData.setPatronsIdList(new ArrayList<>(patronsIdList));
            } else {
                // 其他布阵如果有当前门客，需要移除掉
                Iterator<Integer> iterator = deployData.getPatronsIdList().iterator();
                while (iterator.hasNext()) {
                    Integer patronsId = iterator.next();
                    if (patronsIdList.contains(patronsId)) {
                        iterator.remove();
                    }
                }
            }
        }
        JiuZhouProto.JiuZhouChoosePatronsRespMsg.Builder builder = JiuZhouProto.JiuZhouChoosePatronsRespMsg.newBuilder();
        builder.setRet(0);
        for (CrossJiuZhouDeployData deployData : deployDataList) {
            builder.addDeploy(JiuZhouPb.parseJiuZhouDeployDataMsg(deployData, userData.getPatronsDataMap()));
        }
        sendPacket(Protocol.U_JIU_ZHOU_CHOOSE_PATRONS, builder, userId);
        return 0;
    }

    /**
     * 拉取游戏数据
     *
     * @param userId 用户id
     */
    public int gameDataReqByCmd(long userId, int roomId) {
        CrossJiuZhouRoomData roomData = getRoomData(roomId);
        if (roomData == null) {
            return GameErrorCode.E_JIU_ZHOU_ROOM_NOT_FOUND;
        }
        if (roomData.getState() == eJiuZhouRoomStateType.End.getValue()) {
            return GameErrorCode.E_JIU_ZHOU_GAME_NOT_FOUND;
        }
        if (roomData.getGameData() == null) {
            return GameErrorCode.E_JIU_ZHOU_GAME_NOT_FOUND;
        }
        List<JiuZhouRank> rankList = getRoomRankList(roomId);
        CrossJiuZhouProto.CrossJiuZhouGameDataRespMsg.Builder respMsg = CrossJiuZhouProto.CrossJiuZhouGameDataRespMsg.newBuilder();
        respMsg.setRet(0);
        // 游戏数据
        JiuZhouProto.JiuZhouGameDataMsg.Builder gameMsg = JiuZhouProto.JiuZhouGameDataMsg.newBuilder();
        gameMsg.setRound(roomData.getRound());
        gameMsg.setRoomId(roomData.getRoomId());
        gameMsg.setGameRound(roomData.getGameData().getGameRound());
        // 先移除其他房间我的观战状态
        // 移除所有房间内的这个玩家
        roomDataMap.values().forEach(data -> data.removeWatchUserId(userId));
        // 加入观战列表
        roomData.addWatchUserId(userId);
        int rankNum = 0;
        for (JiuZhouRank jiuZhouRank : rankList) {
            if (jiuZhouRank == null) {
                continue;
            }
            rankNum++;
            CrossJiuZhouGameUserData gameUserData = roomData.getGameData().getGameUserData(jiuZhouRank.getUserId());
            if (gameUserData == null) {
                continue;
            }
            CrossJiuZhouUserData userData = getUserData(gameUserData.getUserId());
            if (userData == null) {
                continue;
            }
            JiuZhouProto.JiuZhouGameUserDataMsg.Builder gameUserMsg = JiuZhouProto.JiuZhouGameUserDataMsg.newBuilder();
            gameUserMsg.setUserId(userData.getUserId());
            CrossUserBaseInfo crossUserBaseInfo = getCrossUserBaseInfo(userData.getUserId());
            if (crossUserBaseInfo != null) {
                gameUserMsg.setUserBaseInfo(PlayerBasePb.parsePlayerBaseTempMsg(crossUserBaseInfo.getUserBaseInfo()));
            }
            // 门客信息
            for (CrossJiuZhouPatronsData patronsData : userData.getPatronsDataMap().values()) {
                gameUserMsg.addPatronsData(JiuZhouPb.parsePatronsSimpleTempMsg(patronsData));
            }
            // 当前血量
            gameUserMsg.setHp(gameUserData.getCurHp());
            // 当前排名
            gameUserMsg.setRank(rankNum);
            gameMsg.addUserData(gameUserMsg);

            // 拉一下用户的布阵数据
            JiuZhouProto.JiuZhouGameUserDeployTemp.Builder deployTemp = JiuZhouProto.JiuZhouGameUserDeployTemp.newBuilder();
            deployTemp.setUserId(gameUserData.getUserId());
            List<CrossJiuZhouDeployData> deployDataList = getDeployDataList(roomId, gameUserData.getUserId());
            for (CrossJiuZhouDeployData deployData : deployDataList) {
                deployTemp.addDeploy(JiuZhouPb.parseJiuZhouDeployDataMsg(deployData, userData.getPatronsDataMap()));
            }
            gameMsg.addDeploy(deployTemp);

            // 拉一下人气值
            JiuZhouProto.JiuZhouPopularUserTempMsg.Builder popularUserTemp = JiuZhouProto.JiuZhouPopularUserTempMsg.newBuilder();
            popularUserTemp.setUserId(gameUserData.getUserId());
            popularUserTemp.setPopular(userData.getPopularityMap().getOrDefault(roomData.getRound(), 0L));
            gameMsg.addUserPopular(popularUserTemp);
        }
        gameMsg.setStageType(roomData.getGameData().getStageType());
        gameMsg.setNextStageTime(roomData.getGameData().getNextStageTime());
        // 战场数据
        for (CrossJiuZhouGameBattleData battleData : roomData.getGameData().getBattleDataList()) {
            gameMsg.addBattleList(parseJiuZhouGameBattleDataMsg(battleData));
        }
        respMsg.setGameData(gameMsg);
        // 本轮有资格吗
        CrossJiuZhouUserData myUserData = getUserData(userId);
        if (myUserData == null) {
            respMsg.setAble(false);
        } else {
            if (myUserData.getLastRoundId() == roomData.getRound()) {
                respMsg.setAble(true);
            } else {
                respMsg.setAble(false);
            }
        }
        sendPacket(Protocol.S_JIU_ZHOU_GET_GAME_DATA_FROM_CROSS, respMsg, userId);
        return 0;
    }



    /**
     * 选锦囊
     */
    public int chooseSkillId3ByCmd(long userId, int roomId, int skillId3) {
        CrossJiuZhouUserData userData = getUserData(userId);
        if (userData == null) {
            return GameErrorCode.E_JIU_ZHOU_USER_NOT_FOUND;
        }
        CrossJiuZhouRoomData roomData = getRoomData(roomId);
        if (roomData == null || roomData.getGameData() == null) {
            return GameErrorCode.E_JIU_ZHOU_GAME_NOT_FOUND;
        }
        if (roomData.getGameData().getStageType() != eJiuZhouGameStageType.ChooseSkillId3.getValue()) {
            return GameErrorCode.E_JIU_ZHOU_NOT_CHOOSE_SKILLID3_TIME;
        }
        if (!roomData.getRoomUserIdList().contains(userId)) {
            return GameErrorCode.E_ERROR_PARAMETER;
        }
        JiuZhouSkillConfig skillConfig = config.getSkillConfig(skillId3);
        if (skillConfig == null || skillConfig.getType() != eJiuZhouBigSkillType.Skill3.getValue()) {
            return GameErrorCode.E_ERROR_PARAMETER;
        }
        for (CrossJiuZhouGameBattleData battleData : roomData.getGameData().getBattleDataList()) {
            // 玩家1
            if (battleData.getBattleUser1().getUserType() != eJiuZhouBattleUserType.Mirror.getValue() && battleData.getBattleUser1().getUserId() == userId) {
                if (!battleData.getBattleUser1().getSkillId3List().contains(skillId3)) {
                    return GameErrorCode.E_ERROR_PARAMETER;
                }
                battleData.getBattleUser1().setChooseSkillId3(skillId3);
                break;
            }

            // 玩家2
            if (battleData.getBattleUser2().getUserType() != eJiuZhouBattleUserType.Mirror.getValue() && battleData.getBattleUser2().getUserId() == userId) {
                if (!battleData.getBattleUser2().getSkillId3List().contains(skillId3)) {
                    return GameErrorCode.E_ERROR_PARAMETER;
                }
                battleData.getBattleUser2().setChooseSkillId3(skillId3);
                break;
            }
        }
        JiuZhouProto.JiuZhouChooseSkillId3RespMsg.Builder respMsg = JiuZhouProto.JiuZhouChooseSkillId3RespMsg.newBuilder();
        respMsg.setRet(0);
        sendPacket(Protocol.U_JIU_ZHOU_CHOOSE_SKILLID3, respMsg, userId);
        return 0;
    }

    /**
     * 刷新锦囊
     */
    public int refreshId3ByCmd(long userId, int roomId) {
        CrossJiuZhouUserData userData = getUserData(userId);
        if (userData == null) {
            return GameErrorCode.E_JIU_ZHOU_USER_NOT_FOUND;
        }
        CrossJiuZhouRoomData roomData = getRoomData(roomId);
        if (roomData == null || roomData.getGameData() == null) {
            return GameErrorCode.E_JIU_ZHOU_GAME_NOT_FOUND;
        }
        if (roomData.getGameData().getStageType() != eJiuZhouGameStageType.ChooseSkillId3.getValue()) {
            return GameErrorCode.E_JIU_ZHOU_NOT_CHOOSE_SKILLID3_TIME;
        }
        if (!roomData.getRoomUserIdList().contains(userId)) {
            return GameErrorCode.E_ERROR_PARAMETER;
        }
        // 我的个人信息
        CrossJiuZhouGameBattleUserData battleUser = null;
        // 对手的个人信息
        CrossJiuZhouGameBattleUserData targetBattleUser = null;
        for (CrossJiuZhouGameBattleData battleData : roomData.getGameData().getBattleDataList()) {
            // 玩家1
            if (battleData.getBattleUser1().getUserType() != eJiuZhouBattleUserType.Mirror.getValue() && battleData.getBattleUser1().getUserId() == userId) {
                battleUser = battleData.getBattleUser1();
                targetBattleUser = battleData.getBattleUser2();
                break;
            }

            // 玩家2
            if (battleData.getBattleUser2().getUserType() != eJiuZhouBattleUserType.Mirror.getValue() && battleData.getBattleUser2().getUserId() == userId) {
                battleUser = battleData.getBattleUser2();
                targetBattleUser = battleData.getBattleUser1();
                break;
            }
        }
        if (battleUser == null || targetBattleUser == null) {
            return GameErrorCode.E_JIU_ZHOU_USER_NOT_FOUND;
        }
        // 判断一下次数有没有达到上限
        if (battleUser.getRefreshTimes() >= config.getRefreshSkillId3Num()) {
            return GameErrorCode.E_JIU_ZHOU_REFRESH_SKILLID3_LIMIT;
        }
        // 已选择锦囊
        battleUser.setChooseSkillId3(0);
        // 随机锦囊
        rollChooseSkillId3(roomData, battleUser);

        battleUser.setRefreshTimes(battleUser.getRefreshTimes() + 1);

        CrossJiuZhouProto.CrossJiuZhouRefreshSkillId3RespMsg.Builder respMsg = CrossJiuZhouProto.CrossJiuZhouRefreshSkillId3RespMsg.newBuilder();
        respMsg.setRet(0);
        // 可选锦囊
        for (Integer skillId3 : battleUser.getSkillId3List()) {
            JiuZhouProto.JiuZhouChooseSkillId3Temp.Builder skillId3Temp = JiuZhouProto.JiuZhouChooseSkillId3Temp.newBuilder();
            skillId3Temp.setSkillId(skillId3);
            // 添加技能效果
            handleSkillId3EffectPreview(battleUser, targetBattleUser, skillId3, skillId3Temp);
            respMsg.addChooseSkillId3(skillId3Temp);
        }
        respMsg.setRefreshTimes(battleUser.getRefreshTimes());
        sendPacket(Protocol.S_JIU_ZHOU_REFRESH_SKILLID3_FROM_CROSS, respMsg, userId);
        return 0;
    }


    /**
     * 获取排行榜 需要进行分页
     * @param userId
     * @param rankType
     * @param zoneType
     * @return
     */
    public int getRankList(long userId,int rankType, int zoneType,int pageNo,int pageSize){
        JiuZhouProto.JiuZhouGetRankRespMsg.Builder builder = JiuZhouProto.JiuZhouGetRankRespMsg.newBuilder();
        CrossJiuZhouRankListModel rankListModel = null;
        //类型为1是积分赛排行榜
        if (rankType == 1){
            if (scoreRoundRankMap == null || scoreRoundRankMap.isEmpty()){
                return GameErrorCode.E_JIU_ZHOU_ACTIVITY_DATA_PREPARING;
            }
            rankListModel = scoreRoundRankMap.get(zoneType);

        }else if (rankType == 2){
            if (outRoundRankMap == null || outRoundRankMap.isEmpty()){
                return GameErrorCode.E_JIU_ZHOU_ACTIVITY_DATA_PREPARING;
            }
            rankListModel = outRoundRankMap.get(zoneType);

        }else {
            return GameErrorCode.E_JIU_ZHOU_RANK_TYPE_ERROR;
        }

        if (rankListModel == null){
            return GameErrorCode.E_JIU_ZHOU_ACTIVITY_DATA_PREPARING;
        }
        int beginRank = Math.max(pageNo * pageSize + 1, 1);
        int endRank = Math.min(beginRank + pageSize - 1, rankListModel.getRankMaxNum());

        // 超出榜单了，不返回
        if (beginRank > rankListModel.getRankList().size()) {
            builder.setRet(0);
            sendPacket(ClientProtocol.U_JIU_ZHOU_GET_RANK_REQ,builder,userId);
        }

        int rankNum = 0;
        List<JiuZhouRank> rankList = rankListModel.getRankList();
        //是否把我自己加进去了
        boolean hasAddMyself = false;
        for (JiuZhouRank rank : rankList){
            rankNum++;
            if (rankNum < beginRank) {
                continue;
            }
            if (rankNum > endRank) {
                break;
            }
            JiuZhouProto.JiuZhouRankTemp.Builder temp = parseJiuZhouRankTemp(rank.getUserId(),rank.getValue(),rank.getParam(),rankNum);
            builder.addRankList(temp);
            if (rank.getUserId() == userId){
                //如果是自己，就把自己的排名加进去
                hasAddMyself = true;
                builder.setMyRank(temp);
            }
        }
        //如果本次回包没有自己的排名 遍历所有去找一下
        if (!hasAddMyself){
            Map<Integer, JiuZhouRank> myRankAndRankInfo = rankListModel.getMyRankAndRankInfo(userId);
            if (myRankAndRankInfo != null && !myRankAndRankInfo.isEmpty()){
                for (Map.Entry<Integer, JiuZhouRank> entry: myRankAndRankInfo.entrySet()){
                    JiuZhouRank myRankInfo = entry.getValue();
                    if(myRankInfo==null){
                        continue;
                    }
                    int myRank = entry.getKey();
                    builder.setMyRank(parseJiuZhouRankTemp(myRankInfo.getUserId(),myRankInfo.getValue(),myRankInfo.getParam(),myRank));
                    break;
                }
            }
        }
        builder.setRet(0);
        sendPacket(ClientProtocol.U_JIU_ZHOU_GET_RANK_REQ,builder,userId);

        return 0;
    }

    /**
     * 解析排行成消息
     *
     * @param userId
     * @param value
     * @param param
     * @param rank
     * @return
     */
    public JiuZhouProto.JiuZhouRankTemp.Builder parseJiuZhouRankTemp(long userId, long value, List<Long> param, int rank) {
        JiuZhouProto.JiuZhouRankTemp.Builder temp = JiuZhouProto.JiuZhouRankTemp.newBuilder();
        temp.setUserId(userId);
        CrossUserBaseInfo crossUserBaseInfo = getCrossUserBaseInfo(userId);
        if (crossUserBaseInfo != null) {
            temp.setUserBaseInfo(PlayerBasePb.parsePlayerBaseTempMsg(crossUserBaseInfo.getUserBaseInfo()));
        }
        temp.setValue(value);
        temp.addAllParam(param);
        temp.setRank(rank);

        // 封装一下轮次信息
        CrossJiuZhouUserData userData = getUserData(userId);
        if (userData != null) {
            int maxRoundRecord = 0;
            int roomRank = -1;
            for (CrossJiuZhouUserRoundRecordData recordData : userData.getRecordDataMap().values()) {
                if (recordData.getRound() >= maxRoundRecord) {
                    maxRoundRecord = recordData.getRound();
                    roomRank = recordData.getRoomRank();
                }
            }
            temp.setLastRound(maxRoundRecord);
            temp.setRoomRank(roomRank);
        }
        return temp;
    }

    /**
     * 上榜
     */
    //--------------------------玩家id-----排行榜类型----赛区类型--------分数-------轮次-------------伤害
    public void uploadRank(int rankType, int zoneType, List<JiuZhouRank> ranks) {
        //类型为1是积分赛排行榜
        if (rankType == 1) {
            if (scoreRoundRankMap == null || scoreRoundRankMap.isEmpty()) {
                return;
            }
            CrossJiuZhouRankListModel rankListModel = scoreRoundRankMap.get(zoneType);
            if (rankListModel == null) {
                return;
            }
            rankListModel.batchRankChange(ranks);
        } else if (rankType == 2) { //类型为2是淘汰赛排行榜
            if (outRoundRankMap == null || outRoundRankMap.isEmpty()) {
                return;
            }
            CrossJiuZhouRankListModel rankListModel = outRoundRankMap.get(zoneType);
            if (rankListModel == null) {
                return;
            }
            rankListModel.batchRankChange(ranks);
        }
    }

    /**
     * 竞猜
     */
    public int guess(long userId,int roundId,Map<Integer,Long> guessMap){
        //玩家上传几个竞猜参数就是几个竞猜
        for (int type : guessMap.keySet()) {
            CrossJiuZhouRoundData crossJiuZhouRoundData = roundDataMap.get(type);
            if (crossJiuZhouRoundData == null) {
                return GameErrorCode.E_JIU_ZHOU_ACTIVITY_DATA_PREPARING;
            }
            //如果当前轮次已经结束
            if (crossJiuZhouRoundData.getCurRoundId() > roundId) {
                return GameErrorCode.E_JIU_ZHOU_GUESS_END;
            }
            //如果当前轮次还没开始
            if (crossJiuZhouRoundData.getCurRoundId() < roundId) {
                return GameErrorCode.E_JIU_ZHOU_GUESS_NOT_START;
            }
            //如果当前轮次正在进行中
            if (crossJiuZhouRoundData.getCurRoundId() == roundId) {
                //如果当前轮次还没开始
                if (crossJiuZhouRoundData.getCurRoundState() == eJiuZhouRoundTime.serverMatching.getValue()) {
                    return GameErrorCode.E_JIU_ZHOU_GUESS_NOT_START;
                }
                //如果当前轮次不是备战期不给竞猜
                if (crossJiuZhouRoundData.getCurRoundState() != eJiuZhouRoundTime.prepare.getValue() ) {
                    return GameErrorCode.E_JIU_ZHOU_GUESS_END;
                }
            }
        }



        //拿出轮次竞猜总数据
        CrossJiuZhouGuessData roundCrossJiuZhouGuessData = getRoundCrossJiuZhouGuessDataMap(roundId);
        if (roundCrossJiuZhouGuessData == null){
            return GameErrorCode.E_JIU_ZHOU_ACTIVITY_DATA_PREPARING;
        }
        //如果数据还没生成完 或者生成有误
        if (roundCrossJiuZhouGuessData.getGuessRoomIdMap().size() < config.getOpenGameZoneNum() || roundCrossJiuZhouGuessData.getAllUserGuessNum().size() < config.getOpenGameZoneNum()){
            return GameErrorCode.E_JIU_ZHOU_ACTIVITY_DATA_PREPARING;
        }
        //如果玩家竞猜的参数数量和当前的不一致
        if (roundCrossJiuZhouGuessData.getGuessRoomIdMap().size() != guessMap.size()){
            return GameErrorCode.E_JIU_ZHOU_REQ_PARAM_ERROR;
        }

        //拿出玩家竞猜数据
        CrossJiuZhouUserGuessData crossJiuZhouUserGuessData = userGuessDataMap.get(userId);
        if (crossJiuZhouUserGuessData == null){
            crossJiuZhouUserGuessData = new CrossJiuZhouUserGuessData(config.getActivityId(), userId);
            crossJiuZhouUserGuessData.setInsertOption();
            userGuessDataMap.put(userId,crossJiuZhouUserGuessData);
        }
        //如果玩家已经竞猜过这个场次
        Map<Integer, Map<Integer, Long>> oldGuessDataMap = crossJiuZhouUserGuessData.getGuessDataMap();
        //6.5.0优化 可以允许重复猜了 但是需要先移除被投票玩家的数据

//        if (oldGuessDataMap.containsKey(roundId)){
//            //如果开启的场次都猜了
//            if (oldGuessDataMap.get(roundId)!= null && oldGuessDataMap.get(roundId).size() == config.getOpenGameZoneNum()){
//                return GameErrorCode.E_JIU_ZHOU_GUESS_ALREADY;
//            }
//        }

        JiuZhouProto.JiuZhouGuessRespMsg.Builder builder = JiuZhouProto.JiuZhouGuessRespMsg.newBuilder();
        synchronized (roundCrossJiuZhouGuessData){
            Map<Integer, Map<Long, Integer>> allUserGuessNum = roundCrossJiuZhouGuessData.getAllUserGuessNum();
            for (int guessZone:guessMap.keySet()){
                //获取各个赛区竞猜数据
                Map<Long, Integer> longIntegerMap = allUserGuessNum.get(guessZone);
                if (longIntegerMap == null){
                    return GameErrorCode.E_JIU_ZHOU_REQ_PARAM_ERROR;
                }
                //如果竞猜的玩家id不在竞猜列表中
                if (!longIntegerMap.containsKey(guessMap.get(guessZone))){
                    return GameErrorCode.E_JIU_ZHOU_REQ_PARAM_ERROR;
                }
            }
            //看看玩家之前这轮有没有猜过 如果有就要去扣人数
            Map<Integer, Long> oldRoundGuess = oldGuessDataMap.get(roundId);
            if (oldRoundGuess != null && !oldRoundGuess.isEmpty()){
                roundCrossJiuZhouGuessData.cancelGuess(oldRoundGuess);
            }
            //更新玩家身上的
            crossJiuZhouUserGuessData.guess(roundId,guessMap);
            //更新被猜人身上的
            roundCrossJiuZhouGuessData.guess(guessMap);
            //回包
            for (int guessZone:guessMap.keySet()){
                //获取各个赛区竞猜数据
                Long guessPlayer = guessMap.get(guessZone);
                JiuZhouProto.JiuZhouGuessData.Builder guessData = JiuZhouProto.JiuZhouGuessData.newBuilder();
                guessData.setZoneType(guessZone);
                guessData.setUserId(guessPlayer);
                builder.addGuessData(guessData);
            }
        }
        builder.setRoundId(roundId);
        builder.setRet(0);
        sendPacket(GameProtocol.S_JIU_ZHOU_GUESS_RESULT_FROM_CROSS,builder,userId);
        return 0;
    }

    /**
     * 获取某一轮次的跨服竞猜数据
     */
    public CrossJiuZhouGuessData getRoundCrossJiuZhouGuessDataMap(int roundId){
        if (roundGuessDataMap == null || roundGuessDataMap.isEmpty()){
            return null;
        }
        return roundGuessDataMap.get(roundId);
    }

    /**
     * 获取某一轮次的跨服竞猜奖励
     */
    public int getGuessReward(long userId,int roundId){
        for (eJiuZhouGameZoneType type:eJiuZhouGameZoneType.values()){
            //没开不管
            if(!config.isGameZoneOpen(type.getValue())){
                continue;
            }
            CrossJiuZhouRoundData crossJiuZhouRoundData = roundDataMap.get(type.getValue());
            if (crossJiuZhouRoundData == null){
                return GameErrorCode.E_JIU_ZHOU_ACTIVITY_DATA_PREPARING;
            }
        }



        //拿出玩家竞猜数据
        CrossJiuZhouUserGuessData userGuessData = getUserGuessData(userId);
        if (userGuessData == null){
            return GameErrorCode.E_JIU_ZHOU_GUESS_NOT_START;
        }
        Map<Integer, Map<Integer, Long>> guessDataMap = userGuessData.getGuessDataMap();
        Map<Integer, Long> roundMap = guessDataMap.get(roundId);
        if (roundMap == null || roundMap.size() < config.getOpenGameZoneNum()){
            return GameErrorCode.E_JIU_ZHOU_NO_GUESS_THIS_ROUND;
        }
        //拿出轮次竞猜总数据
        CrossJiuZhouGuessData roundCrossJiuZhouGuessData = getRoundCrossJiuZhouGuessDataMap(roundId);
        if (roundCrossJiuZhouGuessData == null){
            return GameErrorCode.E_JIU_ZHOU_ACTIVITY_DATA_PREPARING;
        }
        // 拿出轮次胜利者数据
        Map<Integer, Long> roomWinnerMap = roundCrossJiuZhouGuessData.getRoomWinnerMap();
        if (roomWinnerMap == null || roomWinnerMap.isEmpty() || roomWinnerMap.size() < config.getOpenGameZoneNum()){
            return GameErrorCode.E_JIU_ZHOU_ACTIVITY_DATA_PREPARING;
        }
        //如果玩家没有竞猜过这个轮次
        if (!guessDataMap.containsKey(roundId)){
            return GameErrorCode.E_JIU_ZHOU_NO_GUESS_THIS_ROUND;
        }
        int rightNum = 0;
        //拿出玩家竞猜数据
        Map<Integer, Long> guessMap = guessDataMap.get(roundId);
        for (Map.Entry<Integer,Long> entry :guessMap.entrySet()){
            if (entry.getValue().equals(roomWinnerMap.get(entry.getKey()))){
                rightNum++;
            }
        }
        JiuZhouProto.JiuZhouGetGuessRewardRespMsg.Builder builder = JiuZhouProto.JiuZhouGetGuessRewardRespMsg.newBuilder();
        builder.setRet(0);
        builder.setRoundId(roundId);
        builder.setRightNum(rightNum);
        sendPacket(GameProtocol.S_JIU_ZHOU_GET_GUESS_REWARD_FROM_CROSS,builder,userId);

        return 0;

    }

    public CrossJiuZhouUserGuessData getUserGuessData(long userId){
        return userGuessDataMap.get(userId);
    }

    /**
     * 获取九州竞猜轮次数据
     * @param roundId
     * @param userId
     * @return
     */
    public int getJiuZhouGuessRoundData(int roundId, long userId){
        if (roundGuessDataMap == null || roundGuessDataMap.isEmpty()){
            return GameErrorCode.E_JIU_ZHOU_ACTIVITY_DATA_PREPARING;
        }
        CrossJiuZhouGuessData crossJiuZhouGuessData = roundGuessDataMap.get(roundId);
        if (crossJiuZhouGuessData == null){
            return GameErrorCode.E_JIU_ZHOU_ACTIVITY_DATA_PREPARING;
        }
        //拿出所有人被竞猜次数
        Map<Integer, Map<Long, Integer>> allUserGuessNum = crossJiuZhouGuessData.getAllUserGuessNum();
        if (allUserGuessNum == null || allUserGuessNum.isEmpty()){
            return GameErrorCode.E_JIU_ZHOU_ACTIVITY_DATA_PREPARING;
        }

        JiuZhouProto.JiuZhouGetGuessRoundDataRespMsg.Builder builder = JiuZhouProto.JiuZhouGetGuessRoundDataRespMsg.newBuilder();
        builder.setRet(0);
        //拿出所有区当前轮次的竞猜数据
        for (Map.Entry<Integer, Map<Long, Integer>> entry : allUserGuessNum.entrySet()){
            JiuZhouProto.JiuZhouZoneGuessRoomDataMsg.Builder roomData = JiuZhouProto.JiuZhouZoneGuessRoomDataMsg.newBuilder();
            roomData.setZoneType(entry.getKey());
            Map<Long, Integer> userGuessNum = entry.getValue();
            for (Map.Entry<Long, Integer> userGuessNumEntry : userGuessNum.entrySet()) {
                // 用户信息
                CrossUserBaseInfo crossUserBaseInfo = getCrossUserBaseInfo(userGuessNumEntry.getKey());
                if (crossUserBaseInfo == null || crossUserBaseInfo.getUserBaseInfo() == null) {
                    continue;
                }
                JiuZhouProto.JiuZhouZoneGuessRoomUserDataMsg.Builder userData = JiuZhouProto.JiuZhouZoneGuessRoomUserDataMsg.newBuilder();
                userData.setUserId(userGuessNumEntry.getKey());
                userData.setGuessNum(userGuessNumEntry.getValue());

                userData.setUserBaseInfo(PlayerBasePb.parsePlayerBaseTempMsg(crossUserBaseInfo.getUserBaseInfo()));
                roomData.addUserData(userData);
            }
            //去拿出当前区的胜利者 如果有的话
            Map<Integer, Long> roomWinnerMap = crossJiuZhouGuessData.getRoomWinnerMap();
            if (roomWinnerMap != null && roomWinnerMap.containsKey(entry.getKey())){
                roomData.setWinnerId(roomWinnerMap.get(entry.getKey()));
            }
            builder.addZoneGuessRoomData(roomData);
        }
        Map<Integer, List<Long>> guessResultMap = crossJiuZhouGuessData.getGuessResultMap();
        if (guessResultMap != null && !guessResultMap.isEmpty()){
            for (Map.Entry<Integer,List<Long>>entry:guessResultMap.entrySet()){
                JiuZhouProto.JiuZhouAllUserGuessRightSum.Builder allUserGuessRightSum = JiuZhouProto.JiuZhouAllUserGuessRightSum.newBuilder();
                allUserGuessRightSum.setRightNum(entry.getKey());
                allUserGuessRightSum.setGuessNum(entry.getValue().size());
                builder.addAllUserGuessRightSum(allUserGuessRightSum);
            }
        }

        sendPacket(ClientProtocol.U_JIU_ZHOU_GET_GUESS_ROUND_DATA,builder,userId);
        return 0;
    }

    /**
     * 获取竞猜正确玩家列表
     */
    public int getJiuZhouGuessRightUserList(int roundId,int rightNum,int pageNum,long userId){
        if (roundGuessDataMap == null || roundGuessDataMap.isEmpty()){
            return GameErrorCode.E_JIU_ZHOU_ACTIVITY_DATA_PREPARING;
        }
        //如果请求竞猜正确的玩家比配置的多 那就是有问题的
        if (rightNum > config.getOpenGameZoneNum()){
            return GameErrorCode.E_JIU_ZHOU_REQ_PARAM_ERROR;
        }

        //拿出所有区当前轮次的竞猜数据
        CrossJiuZhouGuessData crossJiuZhouGuessData = roundGuessDataMap.get(roundId);
        if (crossJiuZhouGuessData == null){
            return GameErrorCode.E_JIU_ZHOU_ACTIVITY_DATA_PREPARING;
        }
        Map<Integer, List<Long>> guessResultMap = crossJiuZhouGuessData.getGuessResultMap();
        //如果没有竞猜结果 就是这个轮次的淘汰赛还没有结束
        if (guessResultMap == null || guessResultMap.isEmpty()){
            return GameErrorCode.E_JIU_ZHOU_ROUND_NO_END;
        }
        List<Long> guessRightUserList = guessResultMap.get(rightNum);
        if (guessRightUserList == null || guessRightUserList.isEmpty()){
            return GameErrorCode.E_JIU_ZHOU_ROUND_NO_END;
        }
        JiuZhouProto.JiuZhouGetGuessRightPlayerListRespMsg.Builder builder = JiuZhouProto.JiuZhouGetGuessRightPlayerListRespMsg.newBuilder();

        //分页 一页十个玩家
        int pageSize = 10;
        int totalPage = guessRightUserList.size() / pageSize;
        if (guessRightUserList.size() % pageSize != 0){
            totalPage += 1;
        }
        //如果请求的第几页大于总页数
        if (pageNum > totalPage){
            builder.setRet(0);
            sendPacket(ClientProtocol.U_JIU_ZHOU_GET_GUESS_RIGHT_PLAYER_LIST,builder,userId);
            return 0;
        }
        //开始的下标 是第几页乘以每页的数量 从0开始
        int startNum = pageNum * pageSize;
        //结束的下标 是开始的下标加上每页的数量和总数量取最小值
        int endNum = Math.min(startNum + pageSize,guessRightUserList.size());
        for (int i = startNum; i < endNum; i++){
            if (i >= guessRightUserList.size()){
                break;
            }
            long guessRightUserId = guessRightUserList.get(i);
            CrossUserBaseInfo crossUserBaseInfo = getCrossUserBaseInfo(guessRightUserId);
            if (crossUserBaseInfo != null) {
                UserBaseInfo userBaseInfo = crossUserBaseInfo.getUserBaseInfo();
                JiuZhouProto.JiuZhouGuessRightPlayerDataMsg.Builder playerData = JiuZhouProto.JiuZhouGuessRightPlayerDataMsg.newBuilder();
                playerData.setUserId(guessRightUserId);
                playerData.setIndex(i);
                playerData.setNickName(userBaseInfo.getNickName());
                playerData.setServerId(userBaseInfo.getServerId());
                builder.addRightPlayerData(playerData);
            }
        }
        builder.setRet(0);
        sendPacket(ClientProtocol.U_JIU_ZHOU_GET_GUESS_RIGHT_PLAYER_LIST,builder,userId);

        return 0;
    }

    /**
     * 服务器上来获取本次活动的冠军
     * @param serverId
     */

    public void serverGetChampion(long serverId){
        if (config == null){
            return;
        }
        CrossJiuZhouProto.CrossJiuZhouGetChampionDataResultMsg.Builder builder = CrossJiuZhouProto.CrossJiuZhouGetChampionDataResultMsg.newBuilder();
        builder.setActivityId(config.getActivityId());
        for (eJiuZhouGameZoneType type:eJiuZhouGameZoneType.values()){
            CrossJiuZhouRoundData roundData = roundDataMap.get(type.getValue());
            if (roundData == null){
                continue;
            }
            long championId = roundData.getChampionId();
            if (championId <= 0){
                continue;
            }
            JiuZhouProto.JiuZhouChampionDataMsg.Builder championBuilder = JiuZhouProto.JiuZhouChampionDataMsg.newBuilder();
            CrossUserBaseInfo crossUserBaseInfo = getCrossUserBaseInfo(championId);
            if (crossUserBaseInfo == null){
                logger.error("九州棋局活动id:{}的冠军:{}玩家信息没有找到",config.getActivityId(),championId);
                continue;
            }
            championBuilder.setZoneType(type.getValue());
            championBuilder.setUserBaseInfo(PlayerBasePb.parsePlayerBaseTempMsg(crossUserBaseInfo.getUserBaseInfo()));
            championBuilder.setTitleId(getZoneChampionTitleItemId(type.getValue()));
            championBuilder.setUserId(championId);
            builder.addChampionData(championBuilder);
        }
        builder.setTimestamp(System.currentTimeMillis());
        YanQuMessage message = YanQuMessageUtils.buildMessage(GameProtocol.S_JIU_ZHOU_GET_CHAMPION_DATA_FROM_CROSS, builder);
        sendPacket(serverId,0,message);
    }

    /**
     * 获取不同赛区冠军的称号数据
     */
    public int getZoneChampionTitleItemId(int zoneType){
        List<ActivityConditionInfo> activityConditionInfoList = ActivityMgr.getActivityConditionInfoList(config.getActivityId());

        int conditionType = 0;
        if (zoneType == eJiuZhouGameZoneType.master.getValue()){
            conditionType = eGamePlayerEventType.JiuZhouMasterOutRoundRank.getValue();
        }else if (zoneType == eJiuZhouGameZoneType.king.getValue()){
            conditionType = eGamePlayerEventType.JiuZhouKingOutRoundRank.getValue();
        }else if (zoneType == eJiuZhouGameZoneType.elite.getValue()) {
            conditionType = eGamePlayerEventType.JiuZhouEliteOutRoundRank.getValue();
        }else {
            return 0;
        }

        for (ActivityConditionInfo activityConditionInfo:activityConditionInfoList){
            //王者的淘汰赛
            if (activityConditionInfo.getType() != conditionType){
                continue;
            }
            //如果赛区等于配置的赛区
//            if (activityConditionInfo.getParamList().get(3).intValue() != zoneType){
//                continue;
//            }


            //如果是冠军
            if (activityConditionInfo.getParamList().get(0).intValue() != 1){
                continue;
            }
            Property reward = activityConditionInfo.getReward();
            for (int goodId : reward.getGoods().keySet()){
                //是饰品
                if (GoodsMgr.isDecorationGoods(goodId)){
                    //有道具信息
                    GoodsInfo info = GoodsMgr.getGoodsInfo(goodId);
                    //是财神称号
                    if (info != null && info.getParamList().size() >= 4 && info.getParamList().get(3).intValue() > 0){
                        return goodId;
                    }
                }
            }
        }
        return 0;
    }



    /**
     * 查看是否需要锁定本次活动的冠军
     */
    public void checkLockActivityChampion(){
        if (config == null){
            return;
        }

        List<Integer> needLockTypeList = new ArrayList<>();
        int finalRound = Integer.MAX_VALUE;


        for (CrossJiuZhouRoundData roundData:roundDataMap.values()){
            int curRoundId = roundData.getCurRoundId();
            JiuZhouRoundConfig jiuZhouRoundConfigById = getJiuZhouRoundConfigById(curRoundId);
            //如果有冠军了就不需要继续了
            if (roundData.getChampionId() != 0){
                continue;
            }
            if (jiuZhouRoundConfigById == null){
                continue;
            }
            //如果不是冠军赛 就不需要锁定
            if (jiuZhouRoundConfigById.getUpRankNum(roundData.getZoneType()) != 0) {
                continue;
            }
            //如果不在展示其 不锁
            if (roundData.getCurRoundState() != eJiuZhouRoundTime.show.getValue()){
                continue;
            }

            finalRound = jiuZhouRoundConfigById.getId();
            List<Integer> timeList = jiuZhouRoundConfigById.getTimeList();
            if (timeList == null || timeList.isEmpty()){
                continue;
            }
            long battleOverTime = getTimeStampAfterBegin(timeList.get(3));
            //如果当前时间大于比赛结束时间 就需要锁定咯
            if (System.currentTimeMillis() > battleOverTime){
                needLockTypeList.add(roundData.getZoneType());
            }

        }
        //如果要
        if (finalRound == Integer.MAX_VALUE) {
            return;
        }
        List<Integer> finalRoomIdList = new ArrayList<>();
        synchronized (roundRoomIdMap) {
            if (roundRoomIdMap.isEmpty()) {
                logger.error("九州棋局活动idMap没有房间,活动id:{}", config.getActivityId());
                return;
            }

            if (!roundRoomIdMap.containsKey(finalRound)) {
                logger.debug("九州棋局活动idMap暂时没有房间,活动id:{}", config.getActivityId());
                return;
            }
            finalRoomIdList = new ArrayList<>(roundRoomIdMap.get(finalRound));
            if (finalRoomIdList.isEmpty()) {
                logger.error("九州棋局活动roomIdList第{}轮 没有房间,活动id:{}", finalRound, config.getActivityId());
                return;
            }
        }
        synchronized (roomDataMap) {
            for (int finalRoomId : finalRoomIdList) {
                CrossJiuZhouRoomData roomData = roomDataMap.get(finalRoomId);
                if (roomData == null) {
                    logger.error("九州棋局活动roomId:{}没有房间数据,活动id:{}", finalRoomId, config.getActivityId());
                    continue;
                }
                //如果不是需要锁定的类型 就不需要锁定
                if (!needLockTypeList.contains(roomData.getZone())) {
                    continue;
                }

                //房间还没打完不锁
                if (roomData.getState() != eJiuZhouRoomStateType.End.getValue()) {
                    continue;
                }
                List<JiuZhouRank> rankList = getRoomRankList(finalRoomId);
                if (rankList.isEmpty()) {
                    logger.error("九州棋局活动roomId:{}没有排行榜数据,活动id:{}", finalRoomId, config.getActivityId());
                    continue;
                }
                JiuZhouRank first = rankList.get(0);
                if (first == null) {
                    logger.error("九州棋局活动roomId:{}没有排行榜第一名数据,活动id:{}", finalRoomId, config.getActivityId());
                    continue;
                }


                CrossJiuZhouRoundData roundData = roundDataMap.get(roomData.getZone());
                if (roundData == null) {
                    logger.error("九州棋局活动roomId:{}没有轮次数据,活动id:{}", finalRoomId, config.getActivityId());
                    continue;
                }
                roundData.setChampionId(first.getUserId());
            }
        }

    }

    /**
     * 获取淘汰赛的房间信息
     */
    public int getOutRoundRoomData(int roundId,long userId){

        JiuZhouProto.JiuZhouGetOutRoundRoomDataRespMsg.Builder builder = JiuZhouProto.JiuZhouGetOutRoundRoomDataRespMsg.newBuilder();
        if (roundRoomIdMap.isEmpty()){
            return GameErrorCode.E_JIU_ZHOU_ACTIVITY_DATA_PREPARING;
        }
        if (!roundRoomIdMap.containsKey(roundId)){
            return GameErrorCode.E_JIU_ZHOU_ACTIVITY_DATA_PREPARING;
        }
        builder.setRet(0);
        builder.setRoundId(roundId);
        List<Integer> roomIdList = roundRoomIdMap.get(roundId);
        for (int roomId : roomIdList) {
            CrossJiuZhouRoomData roomData = roomDataMap.get(roomId);
            if (roomData == null) {
                continue;
            }

            JiuZhouProto.JiuZhouBriefRoomDataMsg.Builder roomBuilder = JiuZhouProto.JiuZhouBriefRoomDataMsg.newBuilder();

            roomBuilder.setRoomId(roomId);
            roomBuilder.setRoomState(roomData.getState());
            roomBuilder.setRoomZoneType(roomData.getZone());

            // 获取房间榜单
            CrossJiuZhouRankListModel rankListModel = roomBattleRankMap.get(roomId);

            for (Long targetUserId : roomData.getRoomUserIdList()) {
                JiuZhouProto.JiuZhouBriefRoomUserDataMsg.Builder userBuilder = JiuZhouProto.JiuZhouBriefRoomUserDataMsg.newBuilder();
                userBuilder.setUserId(targetUserId);
                CrossUserBaseInfo crossUserBaseInfo = getCrossUserBaseInfo(targetUserId);
                if (crossUserBaseInfo == null) {
                    continue;
                }
                userBuilder.setUserBaseInfo(PlayerBasePb.parsePlayerBaseTempMsg(crossUserBaseInfo.getUserBaseInfo()));
                if (rankListModel != null) {
                    userBuilder.setRank(rankListModel.getMyRank(targetUserId));
                } else {
                    userBuilder.setRank(1);
                }
                roomBuilder.addUserBaseInfo(userBuilder);
            }
            builder.addOutRoundRoomData(roomBuilder);
        }
        sendPacket(ClientProtocol.U_JIU_ZHOU_GET_OUT_ROUND_ROOMS,builder,userId);
        return 0;

    }

    /**
     * 加入房间（观战）
     */
    public int joinRoomByCmd(long userId, int roomId) {
        // !!!! 不需要用户数据,但是跨服需要有用户数据,这边需要把用户数据带上来
        CrossJiuZhouRoomData roomData = getRoomData(roomId);
        if (roomData == null) {
            return GameErrorCode.E_JIU_ZHOU_ROOM_NOT_FOUND;
        }
        // 加入观战列表
        roomData.addWatchUserId(userId);

        // 回包
        JiuZhouProto.JiuZhouJoinRoomRespMsg.Builder respMsg = JiuZhouProto.JiuZhouJoinRoomRespMsg.newBuilder();
        respMsg.setRet(0);
        // ！！！观战，不需要个人数据，userId一定为0 ，传其他的会生成数据
        respMsg.setRoom(parseJiuZhouRoomDataMsg(roomData, 0));
        sendPacket(Protocol.U_JIU_ZHOU_JOIN_ROOM, respMsg, userId);
        return 0;
    }

    /**
     * 离开房间
     *
     * @param userId
     * @param roomId 没啥用
     * @return
     */
    public int leaveRoomByCmd(long userId, int roomId) {
//        CrossJiuZhouRoomData roomData = getRoomData(roomId);
//        if (roomData == null) {
//            return GameErrorCode.E_JIU_ZHOU_ROOM_NOT_FOUND;
//        }
        // 移除观战列表
//        roomData.addWatchUserId(userId);
        // 移除所有房间内的这个玩家
        for (CrossJiuZhouRoomData roomData : roomDataMap.values()) {
            roomData.removeWatchUserId(userId);
        }
        return 0;
    }

    /**
     * 获取战报
     */
    public int battleRecordByCmd(long userId, long targetUserId) {
        CrossJiuZhouUserData userData = getUserData(targetUserId);
        if (userData == null) {
            return GameErrorCode.E_JIU_ZHOU_USER_NOT_FOUND;
        }
        JiuZhouProto.JiuZhouBattleRecordRespMsg.Builder respMsg = JiuZhouProto.JiuZhouBattleRecordRespMsg.newBuilder();
        for (Map.Entry<Integer, Integer> entry : userData.getRoomIdMap().entrySet()) {
            Integer roundId = entry.getKey();
            Integer roomId = entry.getValue();
            CrossJiuZhouRoomData roomData = getRoomData(roomId);
            if (roomData == null || roomData.getGameData() == null) {
                continue;
            }
            // 先判断下有没有这一轮的数据
            CrossJiuZhouUserRoundRecordData roundRecordData = userData.getRecordData(roundId);
            if (roundRecordData == null) {
                continue;
            }
            JiuZhouProto.JiuZhouBattleRoundRecordMsg.Builder roundTemp = JiuZhouProto.JiuZhouBattleRoundRecordMsg.newBuilder();
            roundTemp.setRoundId(roundId);
            // 获取战场战报
            List<CrossJiuZhouGameRoundRecordData> roundRecordList = getGameRoundRecordList(roomId, targetUserId);
            for (CrossJiuZhouGameRoundRecordData recordData : roundRecordList) {
                roundTemp.addGame(parseJiuZhouGameRoundRecordMsg(recordData));
            }
            //  获取下排名
            roundTemp.setRank(roundRecordData.getRoomRank());
            roundTemp.setHadReward(roundRecordData.isHadRankReward());
            roundTemp.setScore(roundRecordData.getScore());
            roundTemp.setReward(PropertyHelper.parsePropertyToString(roundRecordData.getRankReward()));
            respMsg.addRoundRecord(roundTemp);
        }
        respMsg.setRet(0);
        respMsg.setLastRoundId(userData.getLastRoundId());
        sendPacket(Protocol.U_JIU_ZHOU_BATTLE_RECORD, respMsg, userId);
        return 0;
    }

    /**
     * 结算数据
     */
    public int settleByCmd(long userId, int roomId) {
        CrossJiuZhouRoomData roomData = getRoomData(roomId);
        if (roomData == null) {
            return GameErrorCode.E_JIU_ZHOU_ROOM_NOT_FOUND;
        }
        // 结算推送
        JiuZhouProto.JiuZhouSettleRespMsg.Builder respMsg = JiuZhouProto.JiuZhouSettleRespMsg.newBuilder();
        respMsg.setRoomId(roomData.getRoomId());
        for (Long targetUserId : roomData.getRoomUserIdList()) {
            CrossJiuZhouUserData userData = getUserData(targetUserId);
            if (userData == null) {
                continue;
            }
            CrossJiuZhouGameUserData gameUserData = roomData.getGameData().getGameUserData(targetUserId);
            if (gameUserData == null) {
                continue;
            }
            CrossUserBaseInfo crossUserBaseInfo = getCrossUserBaseInfo(targetUserId);
            if (crossUserBaseInfo == null) {
                continue;
            }
            // 获取轮次记录
            CrossJiuZhouUserRoundRecordData record = userData.getRecordData(roomData.getRound());
            if (record == null) {
                continue;
            }
            JiuZhouProto.JiuZhouSettleUserMsg.Builder userTemp = JiuZhouProto.JiuZhouSettleUserMsg.newBuilder();
            userTemp.setUserId(targetUserId);
            userTemp.setUserBaseInfo(PlayerBasePb.parsePlayerBaseTempMsg(crossUserBaseInfo.getUserBaseInfo()));
            // 临时排名
            userTemp.setRank(record.getRoomRank());
            userTemp.setAttackDamage(record.getAttackDamage());
            userTemp.setScore(record.getScore());
            // 结算奖励
            userTemp.setReward(PropertyHelper.parsePropertyToString(record.getRankReward()));
            userTemp.setLastGameRound(gameUserData.getLastGameRound());
            userTemp.setHp(gameUserData.getCurHp());
            respMsg.addUser(userTemp);
        }
        respMsg.setRet(0);
        sendPacket(Protocol.U_JIU_ZHOU_SETTLE, respMsg, userId);
        return 0;
    }

    /**
     * 获取排行榜
     * @param userId
     * @param conditionId
     * @return
     */

    public int getRankReward(long userId ,int conditionId){
        ActivityInfo activityInfo = config.getActivityInfo();
        if (activityInfo == null){
            return GameErrorCode.E_JIU_ZHOU_ACTIVITY_NOT_EXIST;
        }
        if (System.currentTimeMillis() <  activityInfo.getEndTime()){
            return GameErrorCode.E_JIU_ZHOU_NO_IN_REWARD_TIME;
        }
        CrossJiuZhouUserData userData = getUserData(userId);
        if (userData == null){
            return GameErrorCode.E_JIU_ZHOU_USER_NOT_FOUND;
        }


        //拿出conditionInfo
        ActivityConditionInfo activityConditionInfo = ActivityMgr.getActivityConditionInfo(activityInfo.getActivityId(), conditionId);
        int myRank = -1;

        if(null != activityConditionInfo) {
            if (    //精英赛区判断
                    (userData.getZoneType() == eJiuZhouGameZoneType.elite.getValue()
                            && activityConditionInfo.getType() != eGamePlayerEventType.JiuZhouEliteScoreRoundRank.getValue()
                            && activityConditionInfo.getType() != eGamePlayerEventType.JiuZhouEliteOutRoundRank.getValue()
                    ) ||    //大师赛区判断
                            (userData.getZoneType() == eJiuZhouGameZoneType.master.getValue()
                                    && activityConditionInfo.getType() != eGamePlayerEventType.JiuZhouMasterScoreRoundRank.getValue()
                                    && activityConditionInfo.getType() != eGamePlayerEventType.JiuZhouMasterOutRoundRank.getValue())
                            ||  //王者赛区判断
                            (userData.getZoneType() == eJiuZhouGameZoneType.king.getValue()
                                    && activityConditionInfo.getType() != eGamePlayerEventType.JiuZhouKingScoreRoundRank.getValue()
                                    && activityConditionInfo.getType() != eGamePlayerEventType.JiuZhouKingOutRoundRank.getValue())
            ) {
                return GameErrorCode.E_JIU_ZHOU_GET_RANK_REWARD_TYPE_ERROR;
            }

            int roundType = eJiuZhouRoundType.noOpen.getValue();
            if (activityConditionInfo.getType() == eGamePlayerEventType.JiuZhouEliteScoreRoundRank.getValue()
                    || activityConditionInfo.getType() == eGamePlayerEventType.JiuZhouMasterScoreRoundRank.getValue()
                    || activityConditionInfo.getType() == eGamePlayerEventType.JiuZhouKingScoreRoundRank.getValue()){
                roundType = eJiuZhouRoundType.score.getValue();
            } else if (activityConditionInfo.getType() == eGamePlayerEventType.JiuZhouEliteOutRoundRank.getValue()
                    || activityConditionInfo.getType() == eGamePlayerEventType.JiuZhouMasterOutRoundRank.getValue()
                    || activityConditionInfo.getType() == eGamePlayerEventType.JiuZhouKingOutRoundRank.getValue()){
                roundType = eJiuZhouRoundType.knockout.getValue();
            } else {
                return GameErrorCode.E_JIU_ZHOU_REQ_PARAM_ERROR;
            }
            myRank = getRankByIdAndType(userId, userData.getZoneType(), roundType);

        }
        CrossJiuZhouProto.CrossJiuZhouGetRankRewardRespMsg.Builder builder = CrossJiuZhouProto.CrossJiuZhouGetRankRewardRespMsg.newBuilder();
        builder.setRet(0);
        builder.setActivityId(config.getActivityId());
        builder.setConditionId(conditionId);
        builder.setRank(myRank);
        sendPacket(GameProtocol.S_JIU_ZHOU_GET_RANK_REWARD_FROM_CROSS,builder,userId);
        return 0;
    }

    /**
     * 看看这个赛区是不是已经死掉了
     * @param data
     * @return
     */

    public boolean checkIsZoneCantOpen(CrossJiuZhouRoundData data){
        if (data == null){
            return true;
        }
        JiuZhouRoundConfig secondRound = getJiuZhouRoundConfigById(2);
        if (secondRound == null){
            return true;
        }
        List<Integer> timeList = secondRound.getTimeList();
        int startBattleTime = timeList.get(1);
        //如果当前第二轮已经开打了
        if(System.currentTimeMillis() > getTimeStampAfterBegin(startBattleTime)){
            //这时候如果还是0轮
            if (data.getCurRoundId() == 0){
                return true;
            }
            //如果第一轮的数据还是没有
            if (data.getCurRoundId() == 1){
                if (data.getCurRoundState() == eJiuZhouRoundTime.serverMatching.getValue()){
                    //那就是死了
                    return true;
                }
            }
        }
        //还活着
        return false;

    }

    public JiuZhouProto.JiuZhouGameBattleDataMsg.Builder parseJiuZhouGameBattleDataMsg(CrossJiuZhouGameBattleData battleData) {
        if (battleData == null) {
            return null;
        }
        JiuZhouProto.JiuZhouGameBattleDataMsg.Builder builder = JiuZhouProto.JiuZhouGameBattleDataMsg.newBuilder();

        builder.setBattleRound(battleData.getBattleRound());

        if (battleData.getBattleUser1() != null) {
            builder.setBattleUser1(parseJiuZhouGameBattleUserDataMsg(battleData.getBattleUser1(), battleData.getBattleUser2()));
        }
        if (battleData.getBattleUser2() != null) {
            builder.setBattleUser2(parseJiuZhouGameBattleUserDataMsg(battleData.getBattleUser2(), battleData.getBattleUser1()));
        }
        return builder;
    }


    public JiuZhouProto.JiuZhouGameBattleDataMsg.Builder parseJiuZhouGameBattleDataMsg(CrossJiuZhouGameBattleRecordData recordData) {
        if (recordData == null) {
            return null;
        }
        JiuZhouProto.JiuZhouGameBattleDataMsg.Builder builder = JiuZhouProto.JiuZhouGameBattleDataMsg.newBuilder();
        builder.setBattleRound(recordData.getBattleRound());
        if (recordData.getBattleUser1() != null) {
            builder.setBattleUser1(parseJiuZhouGameBattleUserDataMsg(recordData.getBattleUser1(), recordData.getBattleUser2()));
        }
        if (recordData.getBattleUser2() != null) {
            builder.setBattleUser2(parseJiuZhouGameBattleUserDataMsg(recordData.getBattleUser2(), recordData.getBattleUser1()));
        }
        return builder;
    }



    public JiuZhouProto.JiuZhouGameBattleUserDataMsg.Builder parseJiuZhouGameBattleUserDataMsg(CrossJiuZhouGameBattleUserData battleUser,CrossJiuZhouGameBattleUserData targetBattleUser) {
        if (battleUser == null) {
            return null;
        }
        JiuZhouProto.JiuZhouGameBattleUserDataMsg.Builder builder = JiuZhouProto.JiuZhouGameBattleUserDataMsg.newBuilder();
        if (battleUser.getUserType() == eJiuZhouBattleUserType.Mirror.getValue()) {
            // 镜像的话两个需要调换
            builder.setUserId(battleUser.getMirrorUserId());
            builder.setMirrorUserId(battleUser.getUserId());
        } else {
            builder.setUserId(battleUser.getUserId());
        }
        // 战斗门客数据
        for (CrossJiuZhouGamePatronsData patronsData : battleUser.getPatronsDataMap().values()) {
            builder.addPatronsData(parseJiuZhouGamePatronsDataMsg(patronsData));
        }
        // 技能列表
        builder.addAllSkillList(battleUser.getSkillIds());
        // 可选锦囊
        for (Integer skillId3 : battleUser.getSkillId3List()) {
            JiuZhouProto.JiuZhouChooseSkillId3Temp.Builder skillId3Temp = JiuZhouProto.JiuZhouChooseSkillId3Temp.newBuilder();
            skillId3Temp.setSkillId(skillId3);
            // 添加技能效果
            handleSkillId3EffectPreview(battleUser, targetBattleUser, skillId3, skillId3Temp);
            builder.addChooseSkillId3(skillId3Temp);
        }
        // 当前血量
        builder.setHp(battleUser.getHp());
        builder.setExtraHp(battleUser.getExtraHp());
        builder.setUserType(battleUser.getUserType());
        builder.setSilverBuff(battleUser.getSilverBuff().toString());
        builder.setRefreshTimes(battleUser.getRefreshTimes());
        builder.setSkillId3(battleUser.getChooseSkillId3());
        return builder;
    }

    /**
     * 预览锦囊效果
     */
    private void handleSkillId3EffectPreview(CrossJiuZhouGameBattleUserData battleUser, CrossJiuZhouGameBattleUserData targetBattleUser, int skillId, JiuZhouProto.JiuZhouChooseSkillId3Temp.Builder skillId3Temp) {
        if (battleUser == null) {
            return;
        }
        JiuZhouSkillConfig skillConfig = config.getSkillConfig(skillId);
        if (skillConfig == null) {
            return;
        }
        List<Integer> paramList = StringUtils.stringToIntegerList(skillConfig.getSkillParam(), "\\|");

        eJiuZhouSkillType eJiuZhouSkillType = com.yanqu.road.entity.enums.activity.jiuzhou.eJiuZhouSkillType.forSkillId(skillId);
        if (eJiuZhouSkillType == null) {
            logger.error("未知技能类型:{}", skillId);
            return;
        }
        // 所有出战的门客
        List<Integer> allPatronsIdList = new ArrayList<>(battleUser.getPatronsDataMap().keySet());

        switch (eJiuZhouSkillType) {
            case Skill101:
            case Skill102:
            case Skill103:
            case Skill104:
            case Skill105:
                // 步步为营
                // 赚钱加成 参数:加成职业|加成百分比
                for (Integer patronId : allPatronsIdList) {
                    PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(patronId);
                    if (patronsInfo == null) {
                        continue;
                    }
                    // 判断一下职业
                    if (paramList.get(0) != 0 && paramList.get(0) != patronsInfo.getOccupation()) {
                        continue;
                    }
                    // 提升赚钱
                    skillId3Temp.addEffect(builderSkillId3EffectTemp(battleUser.getUserId(), patronId, 1));
                }
                break;
            case Skill106:
                for (Integer patronId : allPatronsIdList) {
                    // 提升赚钱
                    skillId3Temp.addEffect(builderSkillId3EffectTemp(battleUser.getUserId(), patronId, 1));
                }
                break;
            case Skill107:
                // 提升赚钱
                skillId3Temp.addEffect(builderSkillId3EffectTemp(battleUser.getUserId(), battleUser.getAttribute(MinAbilityPatronsId), 1));
                break;
            case Skill108:
                // 最弱门客不出战
                skillId3Temp.addEffect(builderSkillId3EffectTemp(battleUser.getUserId(), battleUser.getAttribute(MinAbilityPatronsId), 5));
                break;
            case Skill109:
                // 提升概率
                skillId3Temp.addEffect(builderSkillId3EffectTemp(battleUser.getUserId(), battleUser.getAttribute(MaxAbilityPatronsId), 2));
                break;
            case Skill110:
                // 每次派出目前赚钱最高的门客，但最多只造成1伤害
                skillId3Temp.addEffect(builderSkillId3EffectTemp(battleUser.getUserId(), battleUser.getAttribute(MaxAbilityPatronsId), 5));
                break;
            case Skill111:
                // 敌方最强门客
                skillId3Temp.addEffect(builderSkillId3EffectTemp(targetBattleUser.getUserId(), targetBattleUser.getAttribute(MaxAbilityPatronsId), 5));
                break;
            case Skill112:
                // 无需处理
                break;
            case Skill113:
                // 无需处理
                break;
            case Skill114:
                // 无需处理
                break;
            case Skill115:
                // 降低伤害
                skillId3Temp.addEffect(builderSkillId3EffectTemp(targetBattleUser.getUserId(), targetBattleUser.getAttribute(MaxAbilityPatronsId), 4));
                break;
            case Skill116:
                // 无需处理
                break;
            case Skill117:
                // 加伤害
                for (Integer patronId : allPatronsIdList) {
                    skillId3Temp.addEffect(builderSkillId3EffectTemp(battleUser.getUserId(), patronId, 3));
                }
                break;
            case Skill118:
                // 最弱门客加出战 加伤害
                skillId3Temp.addEffect(builderSkillId3EffectTemp(battleUser.getUserId(), battleUser.getAttribute(MinAbilityPatronsId, 0), 3));
                break;
            case Skill119:
            case Skill120:
            case Skill122:
                // 我方-全体门客
                for (Integer patronId : allPatronsIdList) {
                    skillId3Temp.addEffect(builderSkillId3EffectTemp(battleUser.getUserId(), patronId, 5));
                }
                break;
            case Skill121:
                // 加赚钱
                for (Integer patronId : allPatronsIdList) {
                    skillId3Temp.addEffect(builderSkillId3EffectTemp(battleUser.getUserId(), patronId, 1));
                }
                break;
            default:
                break;

        }

    }

    public JiuZhouProto.JiuZhouSkillId3EffectTemp.Builder builderSkillId3EffectTemp(long userId, int patronsId, int type) {
        JiuZhouProto.JiuZhouSkillId3EffectTemp.Builder builder = JiuZhouProto.JiuZhouSkillId3EffectTemp.newBuilder();
        builder.setUserId(userId);
        builder.setPatronsId(patronsId);
        builder.setType(type);
        return builder;
    }

    public JiuZhouProto.JiuZhouGamePatronsDataMsg.Builder parseJiuZhouGamePatronsDataMsg(CrossJiuZhouGamePatronsData patronsData) {
        if (patronsData == null) {
            return null;
        }
        JiuZhouProto.JiuZhouGamePatronsDataMsg.Builder builder = JiuZhouProto.JiuZhouGamePatronsDataMsg.newBuilder();
        builder.setPatronsId(patronsData.getPatronsId());
        builder.setAbility(patronsData.getAbility().toString());
        return builder;
    }


    /**
     * 领取结算奖励
     *
     * @param userId
     * @param roundId
     * @return
     */
    public int getSettleRewardByCmd(long userId, int roundId) {
        CrossJiuZhouUserData userData = getUserData(userId);
        if (userData == null) {
            return GameErrorCode.E_JIU_ZHOU_USER_NOT_FOUND;
        }
        if (!userData.getRecordDataMap().containsKey(roundId)) {
            return GameErrorCode.E_JIU_ZHOU_ROOM_NOT_FOUND;
        }
        CrossJiuZhouUserRoundRecordData recordData = userData.getRecordData(roundId);
        if (recordData == null) {
            return GameErrorCode.E_JIU_ZHOU_REQ_PARAM_ERROR;
        }
        if (recordData.isHadRankReward()) {
            return GameErrorCode.E_JIU_ZHOU_SETTLE_HAD_REWARD;
        }
        // 改状态,再发奖
        recordData.setHadRankReward(true);
        userData.setUpdateOption();
        CrossJiuZhouProto.CrossJiuZhouGetSettleRewardRespMsg.Builder respMsg = CrossJiuZhouProto.CrossJiuZhouGetSettleRewardRespMsg.newBuilder();
        respMsg.setRet(0);
        respMsg.setReward(PropertyHelper.parsePropertyToString(recordData.getRankReward()));
        sendPacket(Protocol.S_JIU_ZHOU_SETTLE_FROM_CROSS, respMsg, userId);
        return 0;
    }

    /**
     * 修bug用
     */
    public void fixBug(String param) {

    }

    /**
     * 释放内存
     */
    public void releaseMemoryTask() {
        for (CrossJiuZhouRoundData roundData : roundDataMap.values()) {
            if (roundData == null) {
                continue;
            }
            int curRoundId = roundData.getCurRoundId();
            // 保险起见，释放上轮的内存数据
            int maxReleaseRoundId = curRoundId - 1;
            if (maxReleaseRoundId <= 0) {
                continue;
            }
            for (int round = 1; round <= maxReleaseRoundId; round++) {
                List<Integer> list = roundRoomIdMap.get(round);
                if (list != null && !list.isEmpty()) {
                    for (Integer roomId : list) {
                        // 释放布阵内存数据
                        releaseMemoryWithDeploy(roomId);
                        // 释放其他数据（预留）
                        releaseMemoryOther(roomId);
                    }
                }
            }
        }
    }


    /**
     * 释放布阵数据内存
     */
    private void releaseMemoryWithDeploy(int roomId) {
        if (!deployDataMap.containsKey(roomId)) {
            // 已释放或者不存在
            return;
        }
        // 检测下是否可移除（需全部入库）
        Map<Long, List<CrossJiuZhouDeployData>> listMap = deployDataMap.get(roomId);
        for (List<CrossJiuZhouDeployData> list : listMap.values()) {
            for (CrossJiuZhouDeployData deployData : list) {
                if (deployData.getOp() != DataStatus.None) {
                    // 还有数据未入库
                    return;
                }
            }
        }

        // 移除内存
        deployDataMap.remove(roomId);
    }

    /**
     * 释放房间其他数据
     */
    private void releaseMemoryOther(int roomId) {

    }

    /**
     * 聊天同步,这里直接同步给房间所有的人
     *
     * @param roomId     房间id
     * @param chatUserId 所属用户id
     * @param chatMsg    聊天消息
     */
    public void syncChat(int roomId, long chatUserId, WorldMessageProto.WorldMessageTempMsg chatMsg) {
        JiuZhouProto.JiuZhouRoomChatSyncMsg.Builder syncMsg = JiuZhouProto.JiuZhouRoomChatSyncMsg.newBuilder();
        syncMsg.setActivityId(config.getActivityId());
        syncMsg.setRoomId(roomId);
        syncMsg.setChatUserId(chatUserId);
        syncMsg.setMessage(chatMsg);
        // 拉一下场景里的人
        List<Long> list = CrossSceneMgr.getSceneUserList(config.getActivityInfo().getActivityId(),
                        eSceneType.JiuZhouRoomChat.getValue(),
                        getRoomChatSceneKey(roomId, chatUserId),
                        99999)
                .stream().mapToLong(item -> item.getUserId()).boxed().collect(Collectors.toList());

        if (!list.isEmpty()) {
            sendPacketToAll(ClientProtocol.U_JIU_ZHOU_ROOM_CHAT_SYNC, syncMsg, list);
        }
    }

    /**
     * 发聊天
     */
    public void sendChatByCmd(long userId, CrossActivityProto.CrossActivityChatReqMsg.Builder chatMsg) {
        // 拉一下当前这个人的场景
        UserScene userScene = CrossSceneMgr.getUserScene(userId);
        if (userScene == null || userScene.getSceneType() != eSceneType.JiuZhouRoomChat.getValue()) {
            // 不在九州场景中
            return;
        }
        String[] split = userScene.getSceneId().split("-");
        int roomId = Integer.parseInt(split[0]);
        long chatUserId = Long.parseLong(split[1]);
        CrossJiuZhouRoomData roomData = getRoomData(roomId);
        if (roomData == null) {
            // 房间不在了
            return;
        }
        CrossJiuZhouGameUserData gameUserData = roomData.getGameData().getGameUserData(chatUserId);
        if (gameUserData == null || gameUserData.getCurHp() <= 0) {
            // 死了不让发
            return;
        }
        chatLogic.chat(roomId, chatUserId, 1, chatMsg.getMsg());
    }

    /**
     * 拉一下聊天
     *
     * @param roomId 房间id
     */
    public void getChatByCmd(long userId, int roomId, long chatUserId) {
        CrossJiuZhouRoomData roomData = getRoomData(roomId);
        if (roomData == null) {
            return;
        }
        List<WorldMessageProto.WorldMessageTempMsg> dataList = chatLogic.getChatMsg(roomId, chatUserId);

        JiuZhouProto.JiuZhouGetRoomChatRespMsg.Builder respMsg = JiuZhouProto.JiuZhouGetRoomChatRespMsg.newBuilder();
        respMsg.setRet(0);
        UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(userId);
        if (userBaseInfo != null) {
            // 进入场景
            ScenePlayerInfo scenePlayerInfo = new ScenePlayerInfo(userBaseInfo.getServerId(), userId, userBaseInfo.getNickName(), userBaseInfo.getDecoration());
            UserScene userScene = new UserScene(
                    config.getActivityInfo().getActivityId(),
                    eSceneType.JiuZhouRoomChat.getValue(),
                    getRoomChatSceneKey(roomId, chatUserId),
                    userId,
                    scenePlayerInfo,
                    System.currentTimeMillis());
            CrossSceneMgr.enterScene(userScene, false);
        }

        WorldMessageProto.WorldMessageListMsg.Builder worldMessageListMsg = WorldMessageProto.WorldMessageListMsg.newBuilder();
        // 下发聊天内容
        if (dataList.size() > 0) {
            dataList.sort(Comparator.comparingLong(WorldMessageProto.WorldMessageTempMsg::getSendTime));
            worldMessageListMsg.addAllDataList(dataList);
        }

        respMsg.setDataList(worldMessageListMsg);
        sendPacket(userBaseInfo.getServerId(), userId, YanQuMessageUtils.buildMessage(Protocol.U_JIU_ZHOU_GET_ROOM_CHAT, respMsg));
    }

    private String getRoomChatSceneKey(int roomId, long chatUserId) {
        return roomId + "-" + chatUserId;
    }

    public int getPopularityRankByCmd(long userId, int zoneType, int round) {
        JiuZhouRoundConfig roundConfig = config.getRoundConfigMap().get(round);
        if (roundConfig == null || roundConfig.getType() != eJiuZhouRoundType.knockout.getValue()) {
            return GameErrorCode.E_ERROR_PARAMETER;
        }
        JiuZhouProto.JiuZhouPopularRankRespMsg.Builder respMsg = JiuZhouProto.JiuZhouPopularRankRespMsg.newBuilder();
        CrossJiuZhouRankListModel rankListModel = popularityRankMap.getOrDefault(getPopularityRankKey(zoneType, round), new CrossJiuZhouRankListModel());
        int rankNum = 0;
        for (JiuZhouRank rank : rankListModel.getRankList()) {
            CrossJiuZhouUserData userData = getUserData(rank.getUserId());
            if (userData == null) {
                continue;
            }
            rankNum++;
            JiuZhouProto.JiuZhouPopularRankUserMsg.Builder rankTemp = JiuZhouProto.JiuZhouPopularRankUserMsg.newBuilder();
            CrossUserBaseInfo userBaseInfo = getCrossUserBaseInfo(rank.getUserId());
            if (userBaseInfo == null) {
                continue;
            }
            rankTemp.setUserId(rank.getUserId());
            rankTemp.setUserBaseInfo(PlayerBasePb.parsePlayerBaseTempMsg(userBaseInfo.getUserBaseInfo()));
            rankTemp.setRank(rankNum);
            rankTemp.setPopular(rank.getValue());
            rankTemp.setRoomId(userData.getRoomId(round));
            respMsg.addPopular(rankTemp);
        }
        respMsg.setRet(0);
        sendPacket(Protocol.U_JIU_ZHOU_POPULAR_RANK, respMsg, userId);
        return 0;
    }


    /**
     * 声援
     *
     * @return
     */
    public int supportByCmd(long userId) {
        // 拉一下当前这个人的场景
        UserScene userScene = CrossSceneMgr.getUserScene(userId);
        if (userScene == null || userScene.getSceneType() != eSceneType.JiuZhouRoomChat.getValue()) {
            // 不在九州场景中
            return GameErrorCode.E_JIU_ZHOU_NOT_IN_ROOM_CHAT;
        }
        String[] split = userScene.getSceneId().split("-");
        int roomId = Integer.parseInt(split[0]);
        CrossJiuZhouRoomData roomData = getRoomData(roomId);
        if (roomData == null) {
            return GameErrorCode.E_JIU_ZHOU_ROOM_NOT_FOUND;
        }
        long supportUserId = Long.parseLong(split[1]);

        CrossJiuZhouUserData supportUserData = getUserData(supportUserId);
        if (supportUserData == null) {
            return GameErrorCode.E_JIU_ZHOU_USER_NOT_FOUND;
        }
        // 判断是否淘汰
        CrossJiuZhouGameUserData supportGameUserData = roomData.getGameData().getGameUserData(supportUserId);
        if (supportGameUserData == null || supportGameUserData.getCurHp() <= 0) {
            return GameErrorCode.E_JIU_ZHOU_SUPPORT_USER_OUT;
        }
        // 增加声援数
        int supportNum = addSupportNum(roomData.getRound(), userId, supportUserId);

        // 增加人气值
        addPopularity(roomData.getRoomId(), roomData.getRound(), supportUserData);

        // 送礼广播
        if (supportNum >= config.getSupportBroadcastNum()) {
            JiuZhouProto.JiuZhouSupportSyncMsg.Builder supportSyncMsg = JiuZhouProto.JiuZhouSupportSyncMsg.newBuilder();
            supportSyncMsg.setRoomId(roomId);
            supportSyncMsg.setNum(supportNum);
            supportSyncMsg.setChatUserId(supportUserId);
            CrossUserBaseInfo userBaseInfo1 = getCrossUserBaseInfo(userId);
            if (userBaseInfo1 != null) {
                supportSyncMsg.setUser1(PlayerBasePb.parsePlayerBaseTempMsg(userBaseInfo1.getUserBaseInfo()));      // 送的
            }
            CrossUserBaseInfo userBaseInfo2 = getCrossUserBaseInfo(supportUserId);
            if (userBaseInfo2 != null) {
                supportSyncMsg.setUser2(PlayerBasePb.parsePlayerBaseTempMsg(userBaseInfo2.getUserBaseInfo()));      // 收的
            }
            // 拉一下场景里的人
            List<Long> list = CrossSceneMgr.getSceneUserList(config.getActivityInfo().getActivityId(),
                            eSceneType.JiuZhouRoomChat.getValue(),
                            getRoomChatSceneKey(roomId, supportUserId),
                            99999)
                    .stream().mapToLong(item -> item.getUserId()).boxed().collect(Collectors.toList());
            sendPacketToAll(ClientProtocol.U_JIU_ZHOU_SUPPORT_SYNC, supportSyncMsg, list);
        }

        // 回游戏服扣道具吧
        CrossJiuZhouProto.CrossJiuZhouSupportRespMsg.Builder respMsg = CrossJiuZhouProto.CrossJiuZhouSupportRespMsg.newBuilder();
        respMsg.setRet(0);
        // 给个声援奖励
        respMsg.setReward(config.getSupportRepution());
        sendPacket(Protocol.S_JIU_ZHOU_SUPPORT_FROM_CROSS, respMsg, userId);
        return 0;
    }

    /**
     * 待同步列表
     */
    private Map<Integer, HashSet<Long>> needSyncPopularityMap = new ConcurrentHashMap<>();

    public void addPopularity(int roomId, int round, CrossJiuZhouUserData supportUserData) {
        // 增加人气值
        supportUserData.addPopularity(round, 1);
        // 更新人气榜单
        popularityRankChange(round, supportUserData);

        // 使用 compute 方法确保线程安全地更新 HashSet
        needSyncPopularityMap.compute(roomId, (key, userIds) -> {
            if (userIds == null) {
                userIds = new HashSet<>(); // 如果当前 key 不存在，创建新的 set
            }
            userIds.add(supportUserData.getUserId()); // 添加userId 到 set
            return userIds; // 返回更新后的 set
        });
    }

    /**
     * 人气同步定时器
     */
    public void syncPopularityTask() {
        ConcurrentHashMap<Integer, HashSet<Long>> copyMap = new ConcurrentHashMap<>(needSyncPopularityMap);
        needSyncPopularityMap.clear();
        for (Map.Entry<Integer, HashSet<Long>> entry : copyMap.entrySet()) {
            Integer roomId = entry.getKey();
            HashSet<Long> needSyncUserIds = entry.getValue();
            JiuZhouProto.JiuZhouPopularSyncMsg.Builder syncMsg = JiuZhouProto.JiuZhouPopularSyncMsg.newBuilder();
            syncMsg.setRoomId(roomId);
            for (Long syncUserId : needSyncUserIds) {
                CrossJiuZhouUserData userData = getUserData(syncUserId);
                if (userData == null) {
                    continue;
                }
                JiuZhouProto.JiuZhouPopularUserTempMsg.Builder userTemp = JiuZhouProto.JiuZhouPopularUserTempMsg.newBuilder();
                userTemp.setUserId(syncUserId);
                userTemp.setPopular(userData.getPopularityMap().getOrDefault(userData.getLastRoundId(), 0L));
                syncMsg.addUser(userTemp);
            }
            // 同步给房间里的所有人
            sendPacketToAll(ClientProtocol.U_JIU_ZHOU_POPULARITY_SYNC, syncMsg, getRoomUserListSync(roomId));
        }
    }


    /**
     * 更新人气榜单
     * @param round
     * @param userData
     */
    public void popularityRankChange(int round, CrossJiuZhouUserData userData) {
        if (userData == null || userData.getLastRoundId() < round) {
            return;
        }
        // 人气值
        Long popularityValue = userData.getPopularityMap().getOrDefault(round, 0L);
        CrossJiuZhouRankListModel rankListModel = popularityRankMap.getOrDefault(getPopularityRankKey(userData.getZoneType(), round), new CrossJiuZhouRankListModel());
        List<Long> paramList = new ArrayList<>();
        paramList.add(userData.getPatronsAbility().longValue());
        rankListModel.rankChange(new JiuZhouRank(config.getActivityId(), userData.getUserId(), popularityValue, paramList, System.currentTimeMillis()));

    }

    private int addSupportNum(int round, long userId, long supportUserId) {
        CrossJiuZhouUserData supportUserData = getUserData(supportUserId);
        if (supportUserData == null) {
            return 0;
        }
        if (!supportCntMap.containsKey(round)) {
            supportCntMap.put(round, new ConcurrentHashMap<>());
        }
        if (!supportCntMap.get(round).containsKey(userId)) {
            supportCntMap.get(round).put(userId, new ConcurrentHashMap<>());
        }
        supportCntMap.get(round).get(userId).put(supportUserId, supportCntMap.get(round).get(userId).getOrDefault(supportUserId, 0) + 1);

        return supportCntMap.get(round).get(userId).getOrDefault(supportUserId, 0);
    }

    public String getUserAreaParam(long userId) {
        CrossJiuZhouUserData userData = getUserData(userId);
        if (userData == null) {
            return null;
        }

        int round = getCurRoundByUserId(userId);
        int roomIdx = -1;
        int userRoomId = userData.getRoomId(round);
        if (userRoomId != -1) {
            List<Integer> roomIdList = roundRoomIdMap.get(round);

            List<Integer> filterList = new ArrayList<>();
            if (roomIdList != null) {
                for (Integer roomId : roomIdList) {
                    CrossJiuZhouRoomData roomData = getRoomData(roomId);
                    if (roomData == null) {
                        continue;
                    }

                    if (roomData.getZone() != userData.getZoneType()) {
                        continue;
                    }

                    filterList.add(roomId);
                }

                filterList.sort(Comparator.comparingInt(o -> o));
                roomIdx = filterList.indexOf(userRoomId);
            }
        }

        JSONObject json = new JSONObject();
        json.put("area", userData.getZoneType());
        json.put("round", round);
        json.put("roomIdx", roomIdx);
        return json.toJSONString();
    }

    public int getNowRoundRoomId(long userId){
        CrossJiuZhouUserData userData = getUserData(userId);
        if (userData == null) {
            return -1;
        }
        if (roundDataMap == null || roundDataMap.isEmpty()) {
            return -1;
        }
        CrossJiuZhouRoundData roundData = roundDataMap.get(userData.getZoneType());
        if (roundData == null){
            return -1;
        }
        return userData.getRoomId(roundData.getCurRoundId());

    }

    public boolean ableUnion(long targetId) {
        int maxRound = config.getMaxRound();
        if(Config.isDebug()){
            CrossJiuZhouMgr.getLogger().info("=============== {} maxRound {}", config.getActivityId(), maxRound);
        }
        CrossJiuZhouGuessData guessData = roundGuessDataMap.get(maxRound);
        if(guessData != null){
            for (Map.Entry<Integer, Map<Long, Integer>> entry : guessData.getAllUserGuessNum().entrySet()) {
                if(entry.getValue().containsKey(targetId)){
                    return true;
                }
            }
        }
        return false;
    }
}
